/*!
 *
 * MediaElement.js
 * HTML5 <video> and <audio> shim and player
 * http://mediaelementjs.com/
 *
 * Creates a JavaScript object that mimics HTML5 MediaElement API
 * for browsers that don't understand HTML5 or can't play the provided codec
 * Can play MP4 (H.264), Ogg, WebM, FLV, WMV, WMA, ACC, and MP3
 *
 * Copyright 2010-2014, John Dyer (http://j.hn)
 * License: MIT
 *
 */
// Namespace
var mejs = mejs || {};

// version number
mejs.version = '2.18.1'; 


// player number (for missing, same id attr)
mejs.meIndex = 0;

// media types accepted by plugins
mejs.plugins = {
    silverlight: [
        {version: [3,0], types: ['video/mp4','video/m4v','video/mov','video/wmv','audio/wma','audio/m4a','audio/mp3','audio/wav','audio/mpeg']}
    ],
    flash: [
        {version: [9,0,124], types: ['video/mp4','video/m4v','video/mov','video/flv','video/rtmp','video/x-flv','audio/flv','audio/x-flv','audio/mp3','audio/m4a','audio/mpeg', 'video/youtube', 'video/x-youtube', 'video/dailymotion', 'video/x-dailymotion', 'application/x-mpegURL']}
        //,{version: [12,0], types: ['video/webm']} // for future reference (hopefully!)
    ],
    youtube: [
        {version: null, types: ['video/youtube', 'video/x-youtube', 'audio/youtube', 'audio/x-youtube']}
    ],
    vimeo: [
        {version: null, types: ['video/vimeo', 'video/x-vimeo']}
    ]
};

/*
Utility methods
*/
mejs.Utility = {
    encodeUrl: function(url) {
        return encodeURIComponent(url); //.replace(/\?/gi,'%3F').replace(/=/gi,'%3D').replace(/&/gi,'%26');
    },
    escapeHTML: function(s) {
        return s.toString().split('&').join('&amp;').split('<').join('&lt;').split('"').join('&quot;');
    },
    absolutizeUrl: function(url) {
        var el = document.createElement('div');
        el.innerHTML = '<a href="' + this.escapeHTML(url) + '">x</a>';
        return el.firstChild.href;
    },
    getScriptPath: function(scriptNames) {
        var
            i = 0,
            j,
            codePath = '',
            testname = '',
            slashPos,
            filenamePos,
            scriptUrl,
            scriptPath,         
            scriptFilename,
            scripts = document.getElementsByTagName('script'),
            il = scripts.length,
            jl = scriptNames.length;
            
        // go through all <script> tags
        for (; i < il; i++) {
            scriptUrl = scripts[i].src;
            slashPos = scriptUrl.lastIndexOf('/');
            if (slashPos > -1) {
                scriptFilename = scriptUrl.substring(slashPos + 1);
                scriptPath = scriptUrl.substring(0, slashPos + 1);
            } else {
                scriptFilename = scriptUrl;
                scriptPath = '';            
            }
            
            // see if any <script> tags have a file name that matches the 
            for (j = 0; j < jl; j++) {
                testname = scriptNames[j];
                filenamePos = scriptFilename.indexOf(testname);
                if (filenamePos > -1) {
                    codePath = scriptPath;
                    break;
                }
            }
            
            // if we found a path, then break and return it
            if (codePath !== '') {
                break;
            }
        }
        
        // send the best path back
        return codePath;
    },
    /*
     * Calculate the time format to use. We have a default format set in the
     * options but it can be imcomplete. We ajust it according to the media
     * duration.
     *
     * We support format like 'hh:mm:ss:ff'.
     */
    calculateTimeFormat: function(time, options, fps) {
        if (time < 0) {
            time = 0;
        }

        if(typeof fps == 'undefined') {
            fps = 25;
        }

        var format = options.timeFormat,
            firstChar = format[0],
            firstTwoPlaces = (format[1] == format[0]),
            separatorIndex = firstTwoPlaces? 2: 1,
            separator = ':',
            hours = Math.floor(time / 3600) % 24,
            minutes = Math.floor(time / 60) % 60,
            seconds = Math.floor(time % 60),
            frames = Math.floor(((time % 1)*fps).toFixed(3)),
            lis = [
                [frames, 'f'],
                [seconds, 's'],
                [minutes, 'm'],
                [hours, 'h']
            ];

        // Try to get the separator from the format
        if (format.length < separatorIndex) {
            separator = format[separatorIndex];
        }

        var required = false;

        for (var i=0, len=lis.length; i < len; i++) {
            if (format.indexOf(lis[i][1]) !== -1) {
                required=true;
            }
            else if (required) {
                var hasNextValue = false;
                for (var j=i; j < len; j++) {
                    if (lis[j][0] > 0) {
                        hasNextValue = true;
                        break;
                    }
                }

                if (! hasNextValue) {
                    break;
                }

                if (!firstTwoPlaces) {
                    format = firstChar + format;
                }
                format = lis[i][1] + separator + format;
                if (firstTwoPlaces) {
                    format = lis[i][1] + format;
                }
                firstChar = lis[i][1];
            }
        }
        options.currentTimeFormat = format;
    },
    /*
     * Prefix the given number by zero if it is lower than 10.
     */
    twoDigitsString: function(n) {
        if (n < 10) {
            return '0' + n;
        }
        return String(n);
    },
    secondsToTimeCode: function(time, options) {
        if (time < 0) {
            time = 0;
        }

        var fps = options.framesPerSecond;
        if(typeof fps === 'undefined') {
            fps = 25;
        }

        var format = options.currentTimeFormat,
            hours = Math.floor(time / 3600) % 24,
            minutes = Math.floor(time / 60) % 60,
            seconds = Math.floor(time % 60),
            frames = Math.floor(((time % 1)*fps).toFixed(3));
            lis = [
                [frames, 'f'],
                [seconds, 's'],
                [minutes, 'm'],
                [hours, 'h']
            ];

        var res = format;
        for (i=0,len=lis.length; i < len; i++) {
            res = res.replace(lis[i][1]+lis[i][1], this.twoDigitsString(lis[i][0]));
            res = res.replace(lis[i][1], lis[i][0]);
        }
        return res;
    },
    
    timeCodeToSeconds: function(hh_mm_ss_ff, forceHours, showFrameCount, fps){
        if (typeof showFrameCount == 'undefined') {
            showFrameCount=false;
        } else if(typeof fps == 'undefined') {
            fps = 25;
        }
    
        var tc_array = hh_mm_ss_ff.split(":"),
            tc_hh = parseInt(tc_array[0], 10),
            tc_mm = parseInt(tc_array[1], 10),
            tc_ss = parseInt(tc_array[2], 10),
            tc_ff = 0,
            tc_in_seconds = 0;
        
        if (showFrameCount) {
            tc_ff = parseInt(tc_array[3])/fps;
        }
        
        tc_in_seconds = ( tc_hh * 3600 ) + ( tc_mm * 60 ) + tc_ss + tc_ff;
        
        return tc_in_seconds;
    },
    

    convertSMPTEtoSeconds: function (SMPTE) {
        if (typeof SMPTE != 'string') 
            return false;

        SMPTE = SMPTE.replace(',', '.');
        
        var secs = 0,
            decimalLen = (SMPTE.indexOf('.') != -1) ? SMPTE.split('.')[1].length : 0,
            multiplier = 1;
        
        SMPTE = SMPTE.split(':').reverse();
        
        for (var i = 0; i < SMPTE.length; i++) {
            multiplier = 1;
            if (i > 0) {
                multiplier = Math.pow(60, i); 
            }
            secs += Number(SMPTE[i]) * multiplier;
        }
        return Number(secs.toFixed(decimalLen));
    },  
    
    /* borrowed from SWFObject: http://code.google.com/p/swfobject/source/browse/trunk/swfobject/src/swfobject.js#474 */
    removeSwf: function(id) {
        var obj = document.getElementById(id);
        if (obj && /object|embed/i.test(obj.nodeName)) {
            if (mejs.MediaFeatures.isIE) {
                obj.style.display = "none";
                (function(){
                    if (obj.readyState == 4) {
                        mejs.Utility.removeObjectInIE(id);
                    } else {
                        setTimeout(arguments.callee, 10);
                    }
                })();
            } else {
                obj.parentNode.removeChild(obj);
            }
        }
    },
    removeObjectInIE: function(id) {
        var obj = document.getElementById(id);
        if (obj) {
            for (var i in obj) {
                if (typeof obj[i] == "function") {
                    obj[i] = null;
                }
            }
            obj.parentNode.removeChild(obj);
        }       
    }
};


// Core detector, plugins are added below
mejs.PluginDetector = {

    // main public function to test a plug version number PluginDetector.hasPluginVersion('flash',[9,0,125]);
    hasPluginVersion: function(plugin, v) {
        var pv = this.plugins[plugin];
        v[1] = v[1] || 0;
        v[2] = v[2] || 0;
        return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
    },

    // cached values
    nav: window.navigator,
    ua: window.navigator.userAgent.toLowerCase(),

    // stored version numbers
    plugins: [],

    // runs detectPlugin() and stores the version number
    addPlugin: function(p, pluginName, mimeType, activeX, axDetect) {
        this.plugins[p] = this.detectPlugin(pluginName, mimeType, activeX, axDetect);
    },

    // get the version number from the mimetype (all but IE) or ActiveX (IE)
    detectPlugin: function(pluginName, mimeType, activeX, axDetect) {

        var version = [0,0,0],
            description,
            i,
            ax;

        // Firefox, Webkit, Opera
        if (typeof(this.nav.plugins) != 'undefined' && typeof this.nav.plugins[pluginName] == 'object') {
            description = this.nav.plugins[pluginName].description;
            if (description && !(typeof this.nav.mimeTypes != 'undefined' && this.nav.mimeTypes[mimeType] && !this.nav.mimeTypes[mimeType].enabledPlugin)) {
                version = description.replace(pluginName, '').replace(/^\s+/,'').replace(/\sr/gi,'.').split('.');
                for (i=0; i<version.length; i++) {
                    version[i] = parseInt(version[i].match(/\d+/), 10);
                }
            }
        // Internet Explorer / ActiveX
        } else if (typeof(window.ActiveXObject) != 'undefined') {
            try {
                ax = new ActiveXObject(activeX);
                if (ax) {
                    version = axDetect(ax);
                }
            }
            catch (e) { }
        }
        return version;
    }
};

// Add Flash detection
mejs.PluginDetector.addPlugin('flash','Shockwave Flash','application/x-shockwave-flash','ShockwaveFlash.ShockwaveFlash', function(ax) {
    // adapted from SWFObject
    var version = [],
        d = ax.GetVariable("$version");
    if (d) {
        d = d.split(" ")[1].split(",");
        version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
    }
    return version;
});

// Add Silverlight detection
mejs.PluginDetector.addPlugin('silverlight','Silverlight Plug-In','application/x-silverlight-2','AgControl.AgControl', function (ax) {
    // Silverlight cannot report its version number to IE
    // but it does have a isVersionSupported function, so we have to loop through it to get a version number.
    // adapted from http://www.silverlightversion.com/
    var v = [0,0,0,0],
        loopMatch = function(ax, v, i, n) {
            while(ax.isVersionSupported(v[0]+ "."+ v[1] + "." + v[2] + "." + v[3])){
                v[i]+=n;
            }
            v[i] -= n;
        };
    loopMatch(ax, v, 0, 1);
    loopMatch(ax, v, 1, 1);
    loopMatch(ax, v, 2, 10000); // the third place in the version number is usually 5 digits (4.0.xxxxx)
    loopMatch(ax, v, 2, 1000);
    loopMatch(ax, v, 2, 100);
    loopMatch(ax, v, 2, 10);
    loopMatch(ax, v, 2, 1);
    loopMatch(ax, v, 3, 1);

    return v;
});
// add adobe acrobat
/*
PluginDetector.addPlugin('acrobat','Adobe Acrobat','application/pdf','AcroPDF.PDF', function (ax) {
    var version = [],
        d = ax.GetVersions().split(',')[0].split('=')[1].split('.');

    if (d) {
        version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
    }
    return version;
});
*/
// necessary detection (fixes for <IE9)
mejs.MediaFeatures = {
    init: function() {
        var
            t = this,
            d = document,
            nav = mejs.PluginDetector.nav,
            ua = mejs.PluginDetector.ua.toLowerCase(),
            i,
            v,
            html5Elements = ['source','track','audio','video'];

        // detect browsers (only the ones that have some kind of quirk we need to work around)
        t.isiPad = (ua.match(/ipad/i) !== null);
        t.isiPhone = (ua.match(/iphone/i) !== null);
        t.isiOS = t.isiPhone || t.isiPad;
        t.isAndroid = (ua.match(/android/i) !== null);
        t.isBustedAndroid = (ua.match(/android 2\.[12]/) !== null);
        t.isBustedNativeHTTPS = (location.protocol === 'https:' && (ua.match(/android [12]\./) !== null || ua.match(/macintosh.* version.* safari/) !== null));
        t.isIE = (nav.appName.toLowerCase().indexOf("microsoft") != -1 || nav.appName.toLowerCase().match(/trident/gi) !== null);
        t.isChrome = (ua.match(/chrome/gi) !== null);
        t.isChromium = (ua.match(/chromium/gi) !== null);
        t.isFirefox = (ua.match(/firefox/gi) !== null);
        t.isWebkit = (ua.match(/webkit/gi) !== null);
        t.isGecko = (ua.match(/gecko/gi) !== null) && !t.isWebkit && !t.isIE;
        t.isOpera = (ua.match(/opera/gi) !== null);
        t.hasTouch = ('ontouchstart' in window); //  && window.ontouchstart != null); // this breaks iOS 7
        
        // borrowed from Modernizr
        t.svg = !! document.createElementNS &&
                !! document.createElementNS('http://www.w3.org/2000/svg','svg').createSVGRect;

        // create HTML5 media elements for IE before 9, get a <video> element for fullscreen detection
        for (i=0; i<html5Elements.length; i++) {
            v = document.createElement(html5Elements[i]);
        }
        
        t.supportsMediaTag = (typeof v.canPlayType !== 'undefined' || t.isBustedAndroid);

        // Fix for IE9 on Windows 7N / Windows 7KN (Media Player not installer)
        try{
            v.canPlayType("video/mp4");
        }catch(e){
            t.supportsMediaTag = false;
        }

        // detect native JavaScript fullscreen (Safari/Firefox only, Chrome still fails)
        
        // iOS
        t.hasSemiNativeFullScreen = (typeof v.webkitEnterFullscreen !== 'undefined');
        
        // W3C
        t.hasNativeFullscreen = (typeof v.requestFullscreen !== 'undefined');
        
        // webkit/firefox/IE11+
        t.hasWebkitNativeFullScreen = (typeof v.webkitRequestFullScreen !== 'undefined');
        t.hasMozNativeFullScreen = (typeof v.mozRequestFullScreen !== 'undefined');
        t.hasMsNativeFullScreen = (typeof v.msRequestFullscreen !== 'undefined');
        
        t.hasTrueNativeFullScreen = (t.hasWebkitNativeFullScreen || t.hasMozNativeFullScreen || t.hasMsNativeFullScreen);
        t.nativeFullScreenEnabled = t.hasTrueNativeFullScreen;
        
        // Enabled?
        if (t.hasMozNativeFullScreen) {
            t.nativeFullScreenEnabled = document.mozFullScreenEnabled;
        } else if (t.hasMsNativeFullScreen) {
            t.nativeFullScreenEnabled = document.msFullscreenEnabled;       
        }
        
        if (t.isChrome) {
            t.hasSemiNativeFullScreen = false;
        }
        
        if (t.hasTrueNativeFullScreen) {
            
            t.fullScreenEventName = '';
            if (t.hasWebkitNativeFullScreen) { 
                t.fullScreenEventName = 'webkitfullscreenchange';
                
            } else if (t.hasMozNativeFullScreen) {
                t.fullScreenEventName = 'mozfullscreenchange';
                
            } else if (t.hasMsNativeFullScreen) {
                t.fullScreenEventName = 'MSFullscreenChange';
            }
            
            t.isFullScreen = function() {
                if (t.hasMozNativeFullScreen) {
                    return d.mozFullScreen;
                
                } else if (t.hasWebkitNativeFullScreen) {
                    return d.webkitIsFullScreen;
                
                } else if (t.hasMsNativeFullScreen) {
                    return d.msFullscreenElement !== null;
                }
            }
                    
            t.requestFullScreen = function(el) {
        
                if (t.hasWebkitNativeFullScreen) {
                    el.webkitRequestFullScreen();
                    
                } else if (t.hasMozNativeFullScreen) {
                    el.mozRequestFullScreen();

                } else if (t.hasMsNativeFullScreen) {
                    el.msRequestFullscreen();

                }
            }
            
            t.cancelFullScreen = function() {               
                if (t.hasWebkitNativeFullScreen) {
                    document.webkitCancelFullScreen();
                    
                } else if (t.hasMozNativeFullScreen) {
                    document.mozCancelFullScreen();
                    
                } else if (t.hasMsNativeFullScreen) {
                    document.msExitFullscreen();
                    
                }
            }   
            
        }
        
        
        // OS X 10.5 can't do this even if it says it can :(
        if (t.hasSemiNativeFullScreen && ua.match(/mac os x 10_5/i)) {
            t.hasNativeFullScreen = false;
            t.hasSemiNativeFullScreen = false;
        }
        
    }
};
mejs.MediaFeatures.init();

/*
extension methods to <video> or <audio> object to bring it into parity with PluginMediaElement (see below)
*/
mejs.HtmlMediaElement = {
    pluginType: 'native',
    isFullScreen: false,

    setCurrentTime: function (time) {
        this.currentTime = time;
    },

    setMuted: function (muted) {
        this.muted = muted;
    },

    setVolume: function (volume) {
        this.volume = volume;
    },

    // for parity with the plugin versions
    stop: function () {
        this.pause();
    },

    // This can be a url string
    // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
    setSrc: function (url) {
        
        // Fix for IE9 which can't set .src when there are <source> elements. Awesome, right?
        var 
            existingSources = this.getElementsByTagName('source');
        while (existingSources.length > 0){
            this.removeChild(existingSources[0]);
        }
    
        if (typeof url == 'string') {
            this.src = url;
        } else {
            var i, media;

            for (i=0; i<url.length; i++) {
                media = url[i];
                if (this.canPlayType(media.type)) {
                    this.src = media.src;
                    break;
                }
            }
        }
    },

    setVideoSize: function (width, height) {
        this.width = width;
        this.height = height;
    }
};

/*
Mimics the <video/audio> element by calling Flash's External Interface or Silverlights [ScriptableMember]
*/
mejs.PluginMediaElement = function (pluginid, pluginType, mediaUrl) {
    this.id = pluginid;
    this.pluginType = pluginType;
    this.src = mediaUrl;
    this.events = {};
    this.attributes = {};
};

// JavaScript values and ExternalInterface methods that match HTML5 video properties methods
// http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/fl/video/FLVPlayback.html
// http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
mejs.PluginMediaElement.prototype = {

    // special
    pluginElement: null,
    pluginType: '',
    isFullScreen: false,

    // not implemented :(
    playbackRate: -1,
    defaultPlaybackRate: -1,
    seekable: [],
    played: [],

    // HTML5 read-only properties
    paused: true,
    ended: false,
    seeking: false,
    duration: 0,
    error: null,
    tagName: '',

    // HTML5 get/set properties, but only set (updated by event handlers)
    muted: false,
    volume: 1,
    currentTime: 0,

    // HTML5 methods
    play: function () {
        if (this.pluginApi != null) {
            if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
                this.pluginApi.playVideo();
            } else {
                this.pluginApi.playMedia();
            }
            this.paused = false;
        }
    },
    load: function () {
        if (this.pluginApi != null) {
            if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
            } else {
                this.pluginApi.loadMedia();
            }
            
            this.paused = false;
        }
    },
    pause: function () {
        if (this.pluginApi != null) {
            if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
                this.pluginApi.pauseVideo();
            } else {
                this.pluginApi.pauseMedia();
            }           
            
            
            this.paused = true;
        }
    },
    stop: function () {
        if (this.pluginApi != null) {
            if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
                this.pluginApi.stopVideo();
            } else {
                this.pluginApi.stopMedia();
            }   
            this.paused = true;
        }
    },
    canPlayType: function(type) {
        var i,
            j,
            pluginInfo,
            pluginVersions = mejs.plugins[this.pluginType];

        for (i=0; i<pluginVersions.length; i++) {
            pluginInfo = pluginVersions[i];

            // test if user has the correct plugin version
            if (mejs.PluginDetector.hasPluginVersion(this.pluginType, pluginInfo.version)) {

                // test for plugin playback types
                for (j=0; j<pluginInfo.types.length; j++) {
                    // find plugin that can play the type
                    if (type == pluginInfo.types[j]) {
                        return 'probably';
                    }
                }
            }
        }

        return '';
    },
    
    positionFullscreenButton: function(x,y,visibleAndAbove) {
        if (this.pluginApi != null && this.pluginApi.positionFullscreenButton) {
            this.pluginApi.positionFullscreenButton(Math.floor(x),Math.floor(y),visibleAndAbove);
        }
    },
    
    hideFullscreenButton: function() {
        if (this.pluginApi != null && this.pluginApi.hideFullscreenButton) {
            this.pluginApi.hideFullscreenButton();
        }       
    },  
    

    // custom methods since not all JavaScript implementations support get/set

    // This can be a url string
    // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
    setSrc: function (url) {
        if (typeof url == 'string') {
            this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(url));
            this.src = mejs.Utility.absolutizeUrl(url);
        } else {
            var i, media;

            for (i=0; i<url.length; i++) {
                media = url[i];
                if (this.canPlayType(media.type)) {
                    this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(media.src));
                    this.src = mejs.Utility.absolutizeUrl(media.src);
                    break;
                }
            }
        }

    },
    setCurrentTime: function (time) {
        if (this.pluginApi != null) {
            if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
                this.pluginApi.seekTo(time);
            } else {
                this.pluginApi.setCurrentTime(time);
            }               
            
            
            
            this.currentTime = time;
        }
    },
    setVolume: function (volume) {
        if (this.pluginApi != null) {
            // same on YouTube and MEjs
            if (this.pluginType == 'youtube') {
                this.pluginApi.setVolume(volume * 100);
            } else {
                this.pluginApi.setVolume(volume);
            }
            this.volume = volume;
        }
    },
    setMuted: function (muted) {
        if (this.pluginApi != null) {
            if (this.pluginType == 'youtube') {
                if (muted) {
                    this.pluginApi.mute();
                } else {
                    this.pluginApi.unMute();
                }
                this.muted = muted;
                this.dispatchEvent({type:'volumechange'});
            } else {
                this.pluginApi.setMuted(muted);
            }
            this.muted = muted;
        }
    },

    // additional non-HTML5 methods
    setVideoSize: function (width, height) {
        
        //if (this.pluginType == 'flash' || this.pluginType == 'silverlight') {
            if (this.pluginElement && this.pluginElement.style) {
                this.pluginElement.style.width = width + 'px';
                this.pluginElement.style.height = height + 'px';
            }
            if (this.pluginApi != null && this.pluginApi.setVideoSize) {
                this.pluginApi.setVideoSize(width, height);
            }
        //}
    },

    setFullscreen: function (fullscreen) {
        if (this.pluginApi != null && this.pluginApi.setFullscreen) {
            this.pluginApi.setFullscreen(fullscreen);
        }
    },
    
    enterFullScreen: function() {
        if (this.pluginApi != null && this.pluginApi.setFullscreen) {
            this.setFullscreen(true);
        }       
        
    },
    
    exitFullScreen: function() {
        if (this.pluginApi != null && this.pluginApi.setFullscreen) {
            this.setFullscreen(false);
        }
    },  

    // start: fake events
    addEventListener: function (eventName, callback, bubble) {
        this.events[eventName] = this.events[eventName] || [];
        this.events[eventName].push(callback);
    },
    removeEventListener: function (eventName, callback) {
        if (!eventName) { this.events = {}; return true; }
        var callbacks = this.events[eventName];
        if (!callbacks) return true;
        if (!callback) { this.events[eventName] = []; return true; }
        for (var i = 0; i < callbacks.length; i++) {
            if (callbacks[i] === callback) {
                this.events[eventName].splice(i, 1);
                return true;
            }
        }
        return false;
    },  
    dispatchEvent: function (event) {
        var i,
            args,
            callbacks = this.events[event.type];

        if (callbacks) {
            for (i = 0; i < callbacks.length; i++) {
                callbacks[i].apply(this, [event]);
            }
        }
    },
    // end: fake events
    
    // fake DOM attribute methods
    hasAttribute: function(name){
        return (name in this.attributes);  
    },
    removeAttribute: function(name){
        delete this.attributes[name];
    },
    getAttribute: function(name){
        if (this.hasAttribute(name)) {
            return this.attributes[name];
        }
        return '';
    },
    setAttribute: function(name, value){
        this.attributes[name] = value;
    },

    remove: function() {
        mejs.Utility.removeSwf(this.pluginElement.id);
        mejs.MediaPluginBridge.unregisterPluginElement(this.pluginElement.id);
    }
};

// Handles calls from Flash/Silverlight and reports them as native <video/audio> events and properties
mejs.MediaPluginBridge = {

    pluginMediaElements:{},
    htmlMediaElements:{},

    registerPluginElement: function (id, pluginMediaElement, htmlMediaElement) {
        this.pluginMediaElements[id] = pluginMediaElement;
        this.htmlMediaElements[id] = htmlMediaElement;
    },

    unregisterPluginElement: function (id) {
        delete this.pluginMediaElements[id];
        delete this.htmlMediaElements[id];
    },

    // when Flash/Silverlight is ready, it calls out to this method
    initPlugin: function (id) {

        var pluginMediaElement = this.pluginMediaElements[id],
            htmlMediaElement = this.htmlMediaElements[id];

        if (pluginMediaElement) {
            // find the javascript bridge
            switch (pluginMediaElement.pluginType) {
                case "flash":
                    pluginMediaElement.pluginElement = pluginMediaElement.pluginApi = document.getElementById(id);
                    break;
                case "silverlight":
                    pluginMediaElement.pluginElement = document.getElementById(pluginMediaElement.id);
                    pluginMediaElement.pluginApi = pluginMediaElement.pluginElement.Content.MediaElementJS;
                    break;
            }
    
            if (pluginMediaElement.pluginApi != null && pluginMediaElement.success) {
                pluginMediaElement.success(pluginMediaElement, htmlMediaElement);
            }
        }
    },

    // receives events from Flash/Silverlight and sends them out as HTML5 media events
    // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
    fireEvent: function (id, eventName, values) {

        var
            e,
            i,
            bufferedTime,
            pluginMediaElement = this.pluginMediaElements[id];

        if(!pluginMediaElement){
            return;
        }
        
        // fake event object to mimic real HTML media event.
        e = {
            type: eventName,
            target: pluginMediaElement
        };

        // attach all values to element and event object
        for (i in values) {
            pluginMediaElement[i] = values[i];
            e[i] = values[i];
        }

        // fake the newer W3C buffered TimeRange (loaded and total have been removed)
        bufferedTime = values.bufferedTime || 0;

        e.target.buffered = e.buffered = {
            start: function(index) {
                return 0;
            },
            end: function (index) {
                return bufferedTime;
            },
            length: 1
        };

        pluginMediaElement.dispatchEvent(e);
    }
};

/*
Default options
*/
mejs.MediaElementDefaults = {
    // allows testing on HTML5, flash, silverlight
    // auto: attempts to detect what the browser can do
    // auto_plugin: prefer plugins and then attempt native HTML5
    // native: forces HTML5 playback
    // shim: disallows HTML5, will attempt either Flash or Silverlight
    // none: forces fallback view
    mode: 'auto',
    // remove or reorder to change plugin priority and availability
    plugins: ['flash','silverlight','youtube','vimeo'],
    // shows debug errors on screen
    enablePluginDebug: false,
    // use plugin for browsers that have trouble with Basic Authentication on HTTPS sites
    httpsBasicAuthSite: false,
    // overrides the type specified, useful for dynamic instantiation
    type: '',
    // path to Flash and Silverlight plugins
    pluginPath: mejs.Utility.getScriptPath(['mediaelement.js','mediaelement.min.js','mediaelement-and-player.js','mediaelement-and-player.min.js']),
    // name of flash file
    flashName: 'flashmediaelement.swf',
    // streamer for RTMP streaming
    flashStreamer: '',
    // set to 'always' for CDN version
    flashScriptAccess: 'sameDomain',    
    // turns on the smoothing filter in Flash
    enablePluginSmoothing: false,
    // enabled pseudo-streaming (seek) on .mp4 files
    enablePseudoStreaming: false,
    // start query parameter sent to server for pseudo-streaming
    pseudoStreamingStartQueryParam: 'start',
    // name of silverlight file
    silverlightName: 'silverlightmediaelement.xap',
    // default if the <video width> is not specified
    defaultVideoWidth: 480,
    // default if the <video height> is not specified
    defaultVideoHeight: 270,
    // overrides <video width>
    pluginWidth: -1,
    // overrides <video height>
    pluginHeight: -1,
    // additional plugin variables in 'key=value' form
    pluginVars: [], 
    // rate in milliseconds for Flash and Silverlight to fire the timeupdate event
    // larger number is less accurate, but less strain on plugin->JavaScript bridge
    timerRate: 250,
    // initial volume for player
    startVolume: 0.8,
    success: function () { },
    error: function () { }
};

/*
Determines if a browser supports the <video> or <audio> element
and returns either the native element or a Flash/Silverlight version that
mimics HTML5 MediaElement
*/
mejs.MediaElement = function (el, o) {
    return mejs.HtmlMediaElementShim.create(el,o);
};

mejs.HtmlMediaElementShim = {

    create: function(el, o) {
        var
            options = mejs.MediaElementDefaults,
            htmlMediaElement = (typeof(el) == 'string') ? document.getElementById(el) : el,
            tagName = htmlMediaElement.tagName.toLowerCase(),
            isMediaTag = (tagName === 'audio' || tagName === 'video'),
            src = (isMediaTag) ? htmlMediaElement.getAttribute('src') : htmlMediaElement.getAttribute('href'),
            poster = htmlMediaElement.getAttribute('poster'),
            autoplay =  htmlMediaElement.getAttribute('autoplay'),
            preload =  htmlMediaElement.getAttribute('preload'),
            controls =  htmlMediaElement.getAttribute('controls'),
            playback,
            prop;

        // extend options
        for (prop in o) {
            options[prop] = o[prop];
        }

        // clean up attributes
        src =       (typeof src == 'undefined'  || src === null || src == '') ? null : src;     
        poster =    (typeof poster == 'undefined'   || poster === null) ? '' : poster;
        preload =   (typeof preload == 'undefined'  || preload === null || preload === 'false') ? 'none' : preload;
        autoplay =  !(typeof autoplay == 'undefined' || autoplay === null || autoplay === 'false');
        controls =  !(typeof controls == 'undefined' || controls === null || controls === 'false');

        // test for HTML5 and plugin capabilities
        playback = this.determinePlayback(htmlMediaElement, options, mejs.MediaFeatures.supportsMediaTag, isMediaTag, src);
        playback.url = (playback.url !== null) ? mejs.Utility.absolutizeUrl(playback.url) : '';

        if (playback.method == 'native') {
            // second fix for android
            if (mejs.MediaFeatures.isBustedAndroid) {
                htmlMediaElement.src = playback.url;
                htmlMediaElement.addEventListener('click', function() {
                    htmlMediaElement.play();
                }, false);
            }
        
            // add methods to native HTMLMediaElement
            return this.updateNative(playback, options, autoplay, preload);
        } else if (playback.method !== '') {
            // create plugin to mimic HTMLMediaElement
            
            return this.createPlugin( playback,  options, poster, autoplay, preload, controls);
        } else {
            // boo, no HTML5, no Flash, no Silverlight.
            this.createErrorMessage( playback, options, poster );
            
            return this;
        }
    },
    
    determinePlayback: function(htmlMediaElement, options, supportsMediaTag, isMediaTag, src) {
        var
            mediaFiles = [],
            i,
            j,
            k,
            l,
            n,
            type,
            result = { method: '', url: '', htmlMediaElement: htmlMediaElement, isVideo: (htmlMediaElement.tagName.toLowerCase() != 'audio')},
            pluginName,
            pluginVersions,
            pluginInfo,
            dummy,
            media;
            
        // STEP 1: Get URL and type from <video src> or <source src>

        // supplied type overrides <video type> and <source type>
        if (typeof options.type != 'undefined' && options.type !== '') {
            
            // accept either string or array of types
            if (typeof options.type == 'string') {
                mediaFiles.push({type:options.type, url:src});
            } else {
                
                for (i=0; i<options.type.length; i++) {
                    mediaFiles.push({type:options.type[i], url:src});
                }
            }

        // test for src attribute first
        } else if (src !== null) {
            type = this.formatType(src, htmlMediaElement.getAttribute('type'));
            mediaFiles.push({type:type, url:src});

        // then test for <source> elements
        } else {
            // test <source> types to see if they are usable
            for (i = 0; i < htmlMediaElement.childNodes.length; i++) {
                n = htmlMediaElement.childNodes[i];
                if (n.nodeType == 1 && n.tagName.toLowerCase() == 'source') {
                    src = n.getAttribute('src');
                    type = this.formatType(src, n.getAttribute('type'));
                    media = n.getAttribute('media');

                    if (!media || !window.matchMedia || (window.matchMedia && window.matchMedia(media).matches)) {
                        mediaFiles.push({type:type, url:src});
                    }
                }
            }
        }
        
        // in the case of dynamicly created players
        // check for audio types
        if (!isMediaTag && mediaFiles.length > 0 && mediaFiles[0].url !== null && this.getTypeFromFile(mediaFiles[0].url).indexOf('audio') > -1) {
            result.isVideo = false;
        }
        

        // STEP 2: Test for playback method
        
        // special case for Android which sadly doesn't implement the canPlayType function (always returns '')
        if (mejs.MediaFeatures.isBustedAndroid) {
            htmlMediaElement.canPlayType = function(type) {
                return (type.match(/video\/(mp4|m4v)/gi) !== null) ? 'maybe' : '';
            };
        }       
        
        // special case for Chromium to specify natively supported video codecs (i.e. WebM and Theora) 
        if (mejs.MediaFeatures.isChromium) { 
            htmlMediaElement.canPlayType = function(type) { 
                return (type.match(/video\/(webm|ogv|ogg)/gi) !== null) ? 'maybe' : ''; 
            }; 
        }

        // test for native playback first
        if (supportsMediaTag && (options.mode === 'auto' || options.mode === 'auto_plugin' || options.mode === 'native')  && !(mejs.MediaFeatures.isBustedNativeHTTPS && options.httpsBasicAuthSite === true)) {
                        
            if (!isMediaTag) {

                // create a real HTML5 Media Element 
                dummy = document.createElement( result.isVideo ? 'video' : 'audio');            
                htmlMediaElement.parentNode.insertBefore(dummy, htmlMediaElement);
                htmlMediaElement.style.display = 'none';
                
                // use this one from now on
                result.htmlMediaElement = htmlMediaElement = dummy;
            }
                
            for (i=0; i<mediaFiles.length; i++) {
                // normal check
                if (mediaFiles[i].type == "video/m3u8" || htmlMediaElement.canPlayType(mediaFiles[i].type).replace(/no/, '') !== ''
                    // special case for Mac/Safari 5.0.3 which answers '' to canPlayType('audio/mp3') but 'maybe' to canPlayType('audio/mpeg')
                    || htmlMediaElement.canPlayType(mediaFiles[i].type.replace(/mp3/,'mpeg')).replace(/no/, '') !== ''
                    // special case for m4a supported by detecting mp4 support
                    || htmlMediaElement.canPlayType(mediaFiles[i].type.replace(/m4a/,'mp4')).replace(/no/, '') !== '') {
                    result.method = 'native';
                    result.url = mediaFiles[i].url;
                    break;
                }
            }           
            
            if (result.method === 'native') {
                if (result.url !== null) {
                    htmlMediaElement.src = result.url;
                }
            
                // if `auto_plugin` mode, then cache the native result but try plugins.
                if (options.mode !== 'auto_plugin') {
                    return result;
                }
            }
        }

        // if native playback didn't work, then test plugins
        if (options.mode === 'auto' || options.mode === 'auto_plugin' || options.mode === 'shim') {
            for (i=0; i<mediaFiles.length; i++) {
                type = mediaFiles[i].type;

                // test all plugins in order of preference [silverlight, flash]
                for (j=0; j<options.plugins.length; j++) {

                    pluginName = options.plugins[j];
            
                    // test version of plugin (for future features)
                    pluginVersions = mejs.plugins[pluginName];              
                    
                    for (k=0; k<pluginVersions.length; k++) {
                        pluginInfo = pluginVersions[k];
                    
                        // test if user has the correct plugin version
                        
                        // for youtube/vimeo
                        if (pluginInfo.version == null || 
                            
                            mejs.PluginDetector.hasPluginVersion(pluginName, pluginInfo.version)) {

                            // test for plugin playback types
                            for (l=0; l<pluginInfo.types.length; l++) {
                                // find plugin that can play the type
                                if (type.toLowerCase() == pluginInfo.types[l].toLowerCase()) {
                                    result.method = pluginName;
                                    result.url = mediaFiles[i].url;
                                    return result;
                                }
                            }
                        }
                    }
                }
            }
        }
        
        // at this point, being in 'auto_plugin' mode implies that we tried plugins but failed.
        // if we have native support then return that.
        if (options.mode === 'auto_plugin' && result.method === 'native') {
            return result;
        }

        // what if there's nothing to play? just grab the first available
        if (result.method === '' && mediaFiles.length > 0) {
            result.url = mediaFiles[0].url;
        }

        return result;
    },

    formatType: function(url, type) {
        // if no type is supplied, fake it with the extension
        if (url && !type) {     
            return this.getTypeFromFile(url);
        } else {
            // only return the mime part of the type in case the attribute contains the codec
            // see http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#the-source-element
            // `video/mp4; codecs="avc1.42E01E, mp4a.40.2"` becomes `video/mp4`
            
            if (type && ~type.indexOf(';')) {
                return type.substr(0, type.indexOf(';')); 
            } else {
                return type;
            }
        }
    },
    
    getTypeFromFile: function(url) {
        url = url.split('?')[0];
        var
            ext = url.substring(url.lastIndexOf('.') + 1).toLowerCase(),
            av = /(mp4|m4v|ogg|ogv|m3u8|webm|webmv|flv|wmv|mpeg|mov)/gi.test(ext) ? 'video/' : 'audio/';
        return this.getTypeFromExtension(ext, av);
    },
    
    getTypeFromExtension: function(ext, av) {
        av = av || '';
        
        switch (ext) {
            case 'mp4':
            case 'm4v':
            case 'm4a':
            case 'f4v':
            case 'f4a':
                return av + 'mp4';
            case 'flv':
                return av + 'x-flv';
            case 'webm':
            case 'webma':
            case 'webmv':   
                return av + 'webm';
            case 'ogg':
            case 'oga':
            case 'ogv': 
                return av + 'ogg';
            case 'm3u8':
                return 'application/x-mpegurl';
            case 'ts':
                return av + 'mp2t';
            default:
                return av + ext;
        }
    },

    createErrorMessage: function(playback, options, poster) {
        var 
            htmlMediaElement = playback.htmlMediaElement,
            errorContainer = document.createElement('div'),
            errorContent = options.customError;
            
        errorContainer.className = 'me-cannotplay';

        try {
            errorContainer.style.width = htmlMediaElement.width + 'px';
            errorContainer.style.height = htmlMediaElement.height + 'px';
        } catch (e) {}

        if (!errorContent) {
            errorContent = '<a href="' + playback.url + '">';

            if (poster !== '') {
                errorContent += '<img src="' + poster + '" width="100%" height="100%" alt="" />';
            }

            errorContent += '<span>' + mejs.i18n.t('Download File') + '</span></a>';
        }

        errorContainer.innerHTML = errorContent;

        htmlMediaElement.parentNode.insertBefore(errorContainer, htmlMediaElement);
        htmlMediaElement.style.display = 'none';

        options.error(htmlMediaElement);
    },

    createPlugin:function(playback, options, poster, autoplay, preload, controls) {
        var 
            htmlMediaElement = playback.htmlMediaElement,
            width = 1,
            height = 1,
            pluginid = 'me_' + playback.method + '_' + (mejs.meIndex++),
            pluginMediaElement = new mejs.PluginMediaElement(pluginid, playback.method, playback.url),
            container = document.createElement('div'),
            specialIEContainer,
            node,
            initVars;

        // copy tagName from html media element
        pluginMediaElement.tagName = htmlMediaElement.tagName

        // copy attributes from html media element to plugin media element
        for (var i = 0; i < htmlMediaElement.attributes.length; i++) {
            var attribute = htmlMediaElement.attributes[i];
            if (attribute.specified) {
                pluginMediaElement.setAttribute(attribute.name, attribute.value);
            }
        }

        // check for placement inside a <p> tag (sometimes WYSIWYG editors do this)
        node = htmlMediaElement.parentNode;

        while (node !== null && node.tagName != null && node.tagName.toLowerCase() !== 'body' && 
                node.parentNode != null && node.parentNode.tagName != null && node.parentNode.constructor != null && node.parentNode.constructor.name === "ShadowRoot") {
            if (node.parentNode.tagName.toLowerCase() === 'p') {
                node.parentNode.parentNode.insertBefore(node, node.parentNode);
                break;
            }
            node = node.parentNode;
        }

        if (playback.isVideo) {
            width = (options.pluginWidth > 0) ? options.pluginWidth : (options.videoWidth > 0) ? options.videoWidth : (htmlMediaElement.getAttribute('width') !== null) ? htmlMediaElement.getAttribute('width') : options.defaultVideoWidth;
            height = (options.pluginHeight > 0) ? options.pluginHeight : (options.videoHeight > 0) ? options.videoHeight : (htmlMediaElement.getAttribute('height') !== null) ? htmlMediaElement.getAttribute('height') : options.defaultVideoHeight;
        
            // in case of '%' make sure it's encoded
            width = mejs.Utility.encodeUrl(width);
            height = mejs.Utility.encodeUrl(height);
        
        } else {
            if (options.enablePluginDebug) {
                width = 320;
                height = 240;
            }
        }

        // register plugin
        pluginMediaElement.success = options.success;
        mejs.MediaPluginBridge.registerPluginElement(pluginid, pluginMediaElement, htmlMediaElement);

        // add container (must be added to DOM before inserting HTML for IE)
        container.className = 'me-plugin';
        container.id = pluginid + '_container';
        
        if (playback.isVideo) {
                htmlMediaElement.parentNode.insertBefore(container, htmlMediaElement);
        } else {
                document.body.insertBefore(container, document.body.childNodes[0]);
        }

        // flash/silverlight vars
        initVars = [
            'id=' + pluginid,
            'jsinitfunction=' + "mejs.MediaPluginBridge.initPlugin",
            'jscallbackfunction=' + "mejs.MediaPluginBridge.fireEvent",
            'isvideo=' + ((playback.isVideo) ? "true" : "false"),
            'autoplay=' + ((autoplay) ? "true" : "false"),
            'preload=' + preload,
            'width=' + width,
            'startvolume=' + options.startVolume,
            'timerrate=' + options.timerRate,
            'flashstreamer=' + options.flashStreamer,
            'height=' + height,
            'pseudostreamstart=' + options.pseudoStreamingStartQueryParam];

        if (playback.url !== null) {
            if (playback.method == 'flash') {
                initVars.push('file=' + mejs.Utility.encodeUrl(playback.url));
            } else {
                initVars.push('file=' + playback.url);
            }
        }
        if (options.enablePluginDebug) {
            initVars.push('debug=true');
        }
        if (options.enablePluginSmoothing) {
            initVars.push('smoothing=true');
        }
        if (options.enablePseudoStreaming) {
            initVars.push('pseudostreaming=true');
        }
        if (controls) {
            initVars.push('controls=true'); // shows controls in the plugin if desired
        }
        if (options.pluginVars) {
            initVars = initVars.concat(options.pluginVars);
        }       

        switch (playback.method) {
            case 'silverlight':
                container.innerHTML =
'<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" id="' + pluginid + '" name="' + pluginid + '" width="' + width + '" height="' + height + '" class="mejs-shim">' +
'<param name="initParams" value="' + initVars.join(',') + '" />' +
'<param name="windowless" value="true" />' +
'<param name="background" value="black" />' +
'<param name="minRuntimeVersion" value="3.0.0.0" />' +
'<param name="autoUpgrade" value="true" />' +
'<param name="source" value="' + options.pluginPath + options.silverlightName + '" />' +
'</object>';
                    break;

            case 'flash':

                if (mejs.MediaFeatures.isIE) {
                    specialIEContainer = document.createElement('div');
                    container.appendChild(specialIEContainer);
                    specialIEContainer.outerHTML =
'<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="//download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' +
'id="' + pluginid + '" width="' + width + '" height="' + height + '" class="mejs-shim">' +
'<param name="movie" value="' + options.pluginPath + options.flashName + '?x=' + (new Date()) + '" />' +
'<param name="flashvars" value="' + initVars.join('&amp;') + '" />' +
'<param name="quality" value="high" />' +
'<param name="bgcolor" value="#000000" />' +
'<param name="wmode" value="transparent" />' +
'<param name="allowScriptAccess" value="' + options.flashScriptAccess + '" />' +
'<param name="allowFullScreen" value="true" />' +
'<param name="scale" value="default" />' + 
'</object>';

                } else {

                    container.innerHTML =
'<embed id="' + pluginid + '" name="' + pluginid + '" ' +
'play="true" ' +
'loop="false" ' +
'quality="high" ' +
'bgcolor="#000000" ' +
'wmode="transparent" ' +
'allowScriptAccess="' + options.flashScriptAccess + '" ' +
'allowFullScreen="true" ' +
'type="application/x-shockwave-flash" pluginspage="//www.macromedia.com/go/getflashplayer" ' +
'src="' + options.pluginPath + options.flashName + '" ' +
'flashvars="' + initVars.join('&') + '" ' +
'width="' + width + '" ' +
'height="' + height + '" ' +
'scale="default"' + 
'class="mejs-shim"></embed>';
                }
                break;
            
            case 'youtube':
            
                
                var videoId;
                // youtu.be url from share button
                if (playback.url.lastIndexOf("youtu.be") != -1) {
                    videoId = playback.url.substr(playback.url.lastIndexOf('/')+1);
                    if (videoId.indexOf('?') != -1) {
                        videoId = videoId.substr(0, videoId.indexOf('?'));
                    }
                }
                else {
                    videoId = playback.url.substr(playback.url.lastIndexOf('=')+1);
                }
                youtubeSettings = {
                        container: container,
                        containerId: container.id,
                        pluginMediaElement: pluginMediaElement,
                        pluginId: pluginid,
                        videoId: videoId,
                        height: height,
                        width: width    
                    };              
                
                if (mejs.PluginDetector.hasPluginVersion('flash', [10,0,0]) ) {
                    mejs.YouTubeApi.createFlash(youtubeSettings, options);
                } else {
                    mejs.YouTubeApi.enqueueIframe(youtubeSettings);     
                }
                
                break;
            
            // DEMO Code. Does NOT work.
            case 'vimeo':
                var player_id = pluginid + "_player";
                pluginMediaElement.vimeoid = playback.url.substr(playback.url.lastIndexOf('/')+1);
                
                container.innerHTML ='<iframe src="//player.vimeo.com/video/' + pluginMediaElement.vimeoid + '?api=1&portrait=0&byline=0&title=0&player_id=' + player_id + '" width="' + width +'" height="' + height +'" frameborder="0" class="mejs-shim" id="' + player_id + '" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>';
                if (typeof($f) == 'function') { // froogaloop available
                    var player = $f(container.childNodes[0]);
                    
                    player.addEvent('ready', function() {
                        
                        player.playVideo = function() {
                            player.api( 'play' );
                        } 
                        player.stopVideo = function() {
                            player.api( 'unload' );
                        } 
                        player.pauseVideo = function() {
                            player.api( 'pause' );
                        } 
                        player.seekTo = function( seconds ) {
                            player.api( 'seekTo', seconds );
                        }
                        player.setVolume = function( volume ) {
                            player.api( 'setVolume', volume );
                        }
                        player.setMuted = function( muted ) {
                            if( muted ) {
                                player.lastVolume = player.api( 'getVolume' );
                                player.api( 'setVolume', 0 );
                            } else {
                                player.api( 'setVolume', player.lastVolume );
                                delete player.lastVolume;
                            }
                        }                       

                        function createEvent(player, pluginMediaElement, eventName, e) {
                            var event = {
                                type: eventName,
                                target: pluginMediaElement
                            };
                            if (eventName == 'timeupdate') {
                                pluginMediaElement.currentTime = event.currentTime = e.seconds;
                                pluginMediaElement.duration = event.duration = e.duration;
                            }
                            pluginMediaElement.dispatchEvent(event);
                        }

                        player.addEvent('play', function() {
                            createEvent(player, pluginMediaElement, 'play');
                            createEvent(player, pluginMediaElement, 'playing');
                        });

                        player.addEvent('pause', function() {
                            createEvent(player, pluginMediaElement, 'pause');
                        });

                        player.addEvent('finish', function() {
                            createEvent(player, pluginMediaElement, 'ended');
                        });

                        player.addEvent('playProgress', function(e) {
                            createEvent(player, pluginMediaElement, 'timeupdate', e);
                        });

                        pluginMediaElement.pluginElement = container;
                        pluginMediaElement.pluginApi = player;

                        // init mejs
                        mejs.MediaPluginBridge.initPlugin(pluginid);
                    });
                }
                else {
                    console.warn("You need to include froogaloop for vimeo to work");
                }
                break;          
        }
        // hide original element
        htmlMediaElement.style.display = 'none';
        // prevent browser from autoplaying when using a plugin
        htmlMediaElement.removeAttribute('autoplay');

        // FYI: options.success will be fired by the MediaPluginBridge
        
        return pluginMediaElement;
    },

    updateNative: function(playback, options, autoplay, preload) {
        
        var htmlMediaElement = playback.htmlMediaElement,
            m;
        
        
        // add methods to video object to bring it into parity with Flash Object
        for (m in mejs.HtmlMediaElement) {
            htmlMediaElement[m] = mejs.HtmlMediaElement[m];
        }

        /*
        Chrome now supports preload="none"
        if (mejs.MediaFeatures.isChrome) {
        
            // special case to enforce preload attribute (Chrome doesn't respect this)
            if (preload === 'none' && !autoplay) {
            
                // forces the browser to stop loading (note: fails in IE9)
                htmlMediaElement.src = '';
                htmlMediaElement.load();
                htmlMediaElement.canceledPreload = true;

                htmlMediaElement.addEventListener('play',function() {
                    if (htmlMediaElement.canceledPreload) {
                        htmlMediaElement.src = playback.url;
                        htmlMediaElement.load();
                        htmlMediaElement.play();
                        htmlMediaElement.canceledPreload = false;
                    }
                }, false);
            // for some reason Chrome forgets how to autoplay sometimes.
            } else if (autoplay) {
                htmlMediaElement.load();
                htmlMediaElement.play();
            }
        }
        */

        // fire success code
        options.success(htmlMediaElement, htmlMediaElement);
        
        return htmlMediaElement;
    }
};

/*
 - test on IE (object vs. embed)
 - determine when to use iframe (Firefox, Safari, Mobile) vs. Flash (Chrome, IE)
 - fullscreen?
*/

// YouTube Flash and Iframe API
mejs.YouTubeApi = {
    isIframeStarted: false,
    isIframeLoaded: false,
    loadIframeApi: function() {
        if (!this.isIframeStarted) {
            var tag = document.createElement('script');
            tag.src = "//www.youtube.com/player_api";
            var firstScriptTag = document.getElementsByTagName('script')[0];
            firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
            this.isIframeStarted = true;
        }
    },
    iframeQueue: [],
    enqueueIframe: function(yt) {
        
        if (this.isLoaded) {
            this.createIframe(yt);
        } else {
            this.loadIframeApi();
            this.iframeQueue.push(yt);
        }
    },
    createIframe: function(settings) {
        
        var
        pluginMediaElement = settings.pluginMediaElement,   
        player = new YT.Player(settings.containerId, {
            height: settings.height,
            width: settings.width,
            videoId: settings.videoId,
            playerVars: {controls:0},
            events: {
                'onReady': function() {
                    
                    // hook up iframe object to MEjs
                    settings.pluginMediaElement.pluginApi = player;
                    
                    // init mejs
                    mejs.MediaPluginBridge.initPlugin(settings.pluginId);
                    
                    // create timer
                    setInterval(function() {
                        mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'timeupdate');
                    }, 250);                    
                },
                'onStateChange': function(e) {
                    
                    mejs.YouTubeApi.handleStateChange(e.data, player, pluginMediaElement);
                    
                }
            }
        });
    },
    
    createEvent: function (player, pluginMediaElement, eventName) {
        var event = {
            type: eventName,
            target: pluginMediaElement
        };

        if (player && player.getDuration) {
            
            // time 
            pluginMediaElement.currentTime = event.currentTime = player.getCurrentTime();
            pluginMediaElement.duration = event.duration = player.getDuration();
            
            // state
            event.paused = pluginMediaElement.paused;
            event.ended = pluginMediaElement.ended;         
            
            // sound
            event.muted = player.isMuted();
            event.volume = player.getVolume() / 100;
            
            // progress
            event.bytesTotal = player.getVideoBytesTotal();
            event.bufferedBytes = player.getVideoBytesLoaded();
            
            // fake the W3C buffered TimeRange
            var bufferedTime = event.bufferedBytes / event.bytesTotal * event.duration;
            
            event.target.buffered = event.buffered = {
                start: function(index) {
                    return 0;
                },
                end: function (index) {
                    return bufferedTime;
                },
                length: 1
            };

        }
        
        // send event up the chain
        pluginMediaElement.dispatchEvent(event);
    },  
    
    iFrameReady: function() {
        
        this.isLoaded = true;
        this.isIframeLoaded = true;
        
        while (this.iframeQueue.length > 0) {
            var settings = this.iframeQueue.pop();
            this.createIframe(settings);
        }   
    },
    
    // FLASH!
    flashPlayers: {},
    createFlash: function(settings) {
        
        this.flashPlayers[settings.pluginId] = settings;
        
        /*
        settings.container.innerHTML =
            '<object type="application/x-shockwave-flash" id="' + settings.pluginId + '" data="//www.youtube.com/apiplayer?enablejsapi=1&amp;playerapiid=' + settings.pluginId  + '&amp;version=3&amp;autoplay=0&amp;controls=0&amp;modestbranding=1&loop=0" ' +
                'width="' + settings.width + '" height="' + settings.height + '" style="visibility: visible; " class="mejs-shim">' +
                '<param name="allowScriptAccess" value="sameDomain">' +
                '<param name="wmode" value="transparent">' +
            '</object>';
        */

        var specialIEContainer,
            youtubeUrl = '//www.youtube.com/apiplayer?enablejsapi=1&amp;playerapiid=' + settings.pluginId  + '&amp;version=3&amp;autoplay=0&amp;controls=0&amp;modestbranding=1&loop=0';
            
        if (mejs.MediaFeatures.isIE) {
            
            specialIEContainer = document.createElement('div');
            settings.container.appendChild(specialIEContainer);
            specialIEContainer.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="//download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' +
'id="' + settings.pluginId + '" width="' + settings.width + '" height="' + settings.height + '" class="mejs-shim">' +
    '<param name="movie" value="' + youtubeUrl + '" />' +
    '<param name="wmode" value="transparent" />' +
    '<param name="allowScriptAccess" value="' + options.flashScriptAccess + '" />' +
    '<param name="allowFullScreen" value="true" />' +
'</object>';
        } else {
        settings.container.innerHTML =
            '<object type="application/x-shockwave-flash" id="' + settings.pluginId + '" data="' + youtubeUrl + '" ' +
                'width="' + settings.width + '" height="' + settings.height + '" style="visibility: visible; " class="mejs-shim">' +
                '<param name="allowScriptAccess" value="' + options.flashScriptAccess + '">' +
                '<param name="wmode" value="transparent">' +
            '</object>';
        }       
        
    },
    
    flashReady: function(id) {
        var
            settings = this.flashPlayers[id],
            player = document.getElementById(id),
            pluginMediaElement = settings.pluginMediaElement;
        
        // hook up and return to MediaELementPlayer.success 
        pluginMediaElement.pluginApi = 
        pluginMediaElement.pluginElement = player;
        mejs.MediaPluginBridge.initPlugin(id);
        
        // load the youtube video
        player.cueVideoById(settings.videoId);
        
        var callbackName = settings.containerId + '_callback';
        
        window[callbackName] = function(e) {
            mejs.YouTubeApi.handleStateChange(e, player, pluginMediaElement);
        }
        
        player.addEventListener('onStateChange', callbackName);
        
        setInterval(function() {
            mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'timeupdate');
        }, 250);
        
        mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'canplay');
    },
    
    handleStateChange: function(youTubeState, player, pluginMediaElement) {
        switch (youTubeState) {
            case -1: // not started
                pluginMediaElement.paused = true;
                pluginMediaElement.ended = true;
                mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'loadedmetadata');
                //createYouTubeEvent(player, pluginMediaElement, 'loadeddata');
                break;
            case 0:
                pluginMediaElement.paused = false;
                pluginMediaElement.ended = true;
                mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'ended');
                break;
            case 1:
                pluginMediaElement.paused = false;
                pluginMediaElement.ended = false;               
                mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'play');
                mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'playing');
                break;
            case 2:
                pluginMediaElement.paused = true;
                pluginMediaElement.ended = false;               
                mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'pause');
                break;
            case 3: // buffering
                mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'progress');
                break;
            case 5:
                // cued?
                break;                      
            
        }           
        
    }
}
// IFRAME
window.onYouTubePlayerAPIReady = function() {
    mejs.YouTubeApi.iFrameReady();
};
// FLASH
window.onYouTubePlayerReady = function(id) {
    mejs.YouTubeApi.flashReady(id);
};

window.mejs = mejs;
window.MediaElement = mejs.MediaElement;

/*
 * Adds Internationalization and localization to mediaelement.
 *
 * This file does not contain translations, you have to add them manually.
 * The schema is always the same: me-i18n-locale-[IETF-language-tag].js
 *
 * Examples are provided both for german and chinese translation.
 *
 *
 * What is the concept beyond i18n?
 *   http://en.wikipedia.org/wiki/Internationalization_and_localization
 *
 * What langcode should i use?
 *   http://en.wikipedia.org/wiki/IETF_language_tag
 *   https://tools.ietf.org/html/rfc5646
 *
 *
 * License?
 *
 *   The i18n file uses methods from the Drupal project (drupal.js):
 *     - i18n.methods.t() (modified)
 *     - i18n.methods.checkPlain() (full copy)
 *
 *   The Drupal project is (like mediaelementjs) licensed under GPLv2.
 *    - http://drupal.org/licensing/faq/#q1
 *    - https://github.com/johndyer/mediaelement
 *    - http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 *
 *
 * @author
 *   Tim Latz (latz.tim@gmail.com)
 *
 *
 * @params
 *  - context - document, iframe ..
 *  - exports - CommonJS, window ..
 *
 */
;(function(context, exports, undefined) {
    "use strict";

    var i18n = {
        "locale": {
            // Ensure previous values aren't overwritten.
            "language" : (exports.i18n && exports.i18n.locale.language) || '',
            "strings" : (exports.i18n && exports.i18n.locale.strings) || {}
        },
        "ietf_lang_regex" : /^(x\-)?[a-z]{2,}(\-\w{2,})?(\-\w{2,})?$/,
        "methods" : {}
    };
// start i18n


    /**
     * Get language, fallback to browser's language if empty
     *
     * IETF: RFC 5646, https://tools.ietf.org/html/rfc5646
     * Examples: en, zh-CN, cmn-Hans-CN, sr-Latn-RS, es-419, x-private
     */
    i18n.getLanguage = function () {
        var language = i18n.locale.language || window.navigator.userLanguage || window.navigator.language;
        return i18n.ietf_lang_regex.exec(language) ? language : null;

        //(WAS: convert to iso 639-1 (2-letters, lower case))
        //return language.substr(0, 2).toLowerCase();
    };

    // i18n fixes for compatibility with WordPress
    if ( typeof mejsL10n != 'undefined' ) {
        i18n.locale.language = mejsL10n.language;
    }



    /**
     * Encode special characters in a plain-text string for display as HTML.
     */
    i18n.methods.checkPlain = function (str) {
        var character, regex,
        replace = {
            '&': '&amp;',
            '"': '&quot;',
            '<': '&lt;',
            '>': '&gt;'
        };
        str = String(str);
        for (character in replace) {
            if (replace.hasOwnProperty(character)) {
                regex = new RegExp(character, 'g');
                str = str.replace(regex, replace[character]);
            }
        }
        return str;
    };

    /**
     * Translate strings to the page language or a given language.
     *
     *
     * @param str
     *   A string containing the English string to translate.
     *
     * @param options
     *   - 'context' (defaults to the default context): The context the source string
     *     belongs to.
     *
     * @return
     *   The translated string, escaped via i18n.methods.checkPlain()
     */
    i18n.methods.t = function (str, options) {

        // Fetch the localized version of the string.
        if (i18n.locale.strings && i18n.locale.strings[options.context] && i18n.locale.strings[options.context][str]) {
            str = i18n.locale.strings[options.context][str];
        }

        return i18n.methods.checkPlain(str);
    };


    /**
     * Wrapper for i18n.methods.t()
     *
     * @see i18n.methods.t()
     * @throws InvalidArgumentException
     */
    i18n.t = function(str, options) {

        if (typeof str === 'string' && str.length > 0) {

            // check every time due language can change for
            // different reasons (translation, lang switcher ..)
            var language = i18n.getLanguage();

            options = options || {
                "context" : language
            };

            return i18n.methods.t(str, options);
        }
        else {
            throw {
                "name" : 'InvalidArgumentException',
                "message" : 'First argument is either not a string or empty.'
            };
        }
    };

// end i18n
    exports.i18n = i18n;
}(document, mejs));

// i18n fixes for compatibility with WordPress
;(function(exports, undefined) {

    "use strict";

    if ( typeof mejsL10n != 'undefined' ) {
        exports[mejsL10n.language] = mejsL10n.strings;
    }

}(mejs.i18n.locale.strings));

/*!
 *
 * MediaElementPlayer
 * http://mediaelementjs.com/
 *
 * Creates a controller bar for HTML5 <video> add <audio> tags
 * using jQuery and MediaElement.js (HTML5 Flash/Silverlight wrapper)
 *
 * Copyright 2010-2013, John Dyer (http://j.hn/)
 * License: MIT
 *
 */
if (typeof jQuery != 'undefined') {
    mejs.$ = jQuery;
} else if (typeof Zepto != 'undefined') {
    mejs.$ = Zepto;

    // define `outerWidth` method which has not been realized in Zepto
    Zepto.fn.outerWidth = function(includeMargin) {
        var width = $(this).width();
        if (includeMargin) {
            width += parseInt($(this).css('margin-right'), 10);
            width += parseInt($(this).css('margin-left'), 10);
        }
        return width
    }

} else if (typeof ender != 'undefined') {
    mejs.$ = ender;
}
(function ($) {

    // default player values
    mejs.MepDefaults = {
        // url to poster (to fix iOS 3.x)
        poster: '',
        // When the video is ended, we can show the poster.
        showPosterWhenEnded: false,
        // default if the <video width> is not specified
        defaultVideoWidth: 480,
        // default if the <video height> is not specified
        defaultVideoHeight: 270,
        // if set, overrides <video width>
        videoWidth: -1,
        // if set, overrides <video height>
        videoHeight: -1,
        // default if the user doesn't specify
        defaultAudioWidth: 400,
        // default if the user doesn't specify
        defaultAudioHeight: 40,

        // default amount to move back when back key is pressed
        defaultSeekBackwardInterval: function(media) {
            return (media.duration * 0.05);
        },
        // default amount to move forward when forward key is pressed
        defaultSeekForwardInterval: function(media) {
            return (media.duration * 0.05);
        },

        // set dimensions via JS instead of CSS
        setDimensions: true,

        // width of audio player
        audioWidth: -1,
        // height of audio player
        audioHeight: -1,
        // initial volume when the player starts (overrided by user cookie)
        startVolume: 0.8,
        // useful for <audio> player loops
        loop: false,
        // rewind to beginning when media ends
                autoRewind: true,
        // resize to media dimensions
        enableAutosize: true,

        /*
         * Time format to use. Default: 'mm:ss'
         * Supported units:
         *   h: hour
         *   m: minute
         *   s: second
         *   f: frame count
         * When using 'hh', 'mm', 'ss' or 'ff' we always display 2 digits.
         * If you use 'h', 'm', 's' or 'f' we display 1 digit if possible.
         *
         * Example to display 75 seconds:
         * Format 'mm:ss': 01:15
         * Format 'm:ss': 1:15
         * Format 'm:s': 1:15
         */
        timeFormat: '',
        // forces the hour marker (##:00:00)
        alwaysShowHours: false,
        // show framecount in timecode (##:00:00:00)
        showTimecodeFrameCount: false,
        // used when showTimecodeFrameCount is set to true
        framesPerSecond: 25,

        // automatically calculate the width of the progress bar based on the sizes of other elements
        autosizeProgress : true,
        // Hide controls when playing and mouse is not over the video
        alwaysShowControls: false,
        // Display the video control
        hideVideoControlsOnLoad: false,
        // Enable click video element to toggle play/pause
        clickToPlayPause: true,
        // force iPad's native controls
        iPadUseNativeControls: false,
        // force iPhone's native controls
        iPhoneUseNativeControls: false,
        // force Android's native controls
        AndroidUseNativeControls: false,
        // features to show
        features: ['playpause','current','progress','duration','tracks','volume','fullscreen'],
        // only for dynamic
        isVideo: true,

        // turns keyboard support on and off for this instance
        enableKeyboard: true,

        // whenthis player starts, it will pause other players
        pauseOtherPlayers: true,

        // array of keyboard actions such as play pause
        keyActions: [
                {
                        keys: [
                                32, // SPACE
                                179 // GOOGLE play/pause button
                              ],
                        action: function(player, media) {
                                if (media.paused || media.ended) {
                                        media.play();
                                } else {
                                        media.pause();
                                }
                        }
                },
                {
                        keys: [38], // UP
                        action: function(player, media) {
                                player.container.find('.mejs-volume-slider').css('display','block');
                                if (player.isVideo) {
                                        player.showControls();
                                        player.startControlsTimer();
                                }

                                var newVolume = Math.min(media.volume + 0.1, 1);
                                media.setVolume(newVolume);
                        }
                },
                {
                        keys: [40], // DOWN
                        action: function(player, media) {
                                player.container.find('.mejs-volume-slider').css('display','block');
                                if (player.isVideo) {
                                        player.showControls();
                                        player.startControlsTimer();
                                }

                                var newVolume = Math.max(media.volume - 0.1, 0);
                                media.setVolume(newVolume);
                        }
                },
                {
                        keys: [
                                37, // LEFT
                                227 // Google TV rewind
                        ],
                        action: function(player, media) {
                                if (!isNaN(media.duration) && media.duration > 0) {
                                        if (player.isVideo) {
                                                player.showControls();
                                                player.startControlsTimer();
                                        }

                                        // 5%
                                        var newTime = Math.max(media.currentTime - player.options.defaultSeekBackwardInterval(media), 0);
                                        media.setCurrentTime(newTime);
                                }
                        }
                },
                {
                        keys: [
                                39, // RIGHT
                                228 // Google TV forward
                        ],
                        action: function(player, media) {
                                if (!isNaN(media.duration) && media.duration > 0) {
                                        if (player.isVideo) {
                                                player.showControls();
                                                player.startControlsTimer();
                                        }

                                        // 5%
                                        var newTime = Math.min(media.currentTime + player.options.defaultSeekForwardInterval(media), media.duration);
                                        media.setCurrentTime(newTime);
                                }
                        }
                },
                {
                        keys: [70], // F
                        action: function(player, media) {
                                if (typeof player.enterFullScreen != 'undefined') {
                                        if (player.isFullScreen) {
                                                player.exitFullScreen();
                                        } else {
                                                player.enterFullScreen();
                                        }
                                }
                        }
                },
                {
                        keys: [77], // M
                        action: function(player, media) {
                                player.container.find('.mejs-volume-slider').css('display','block');
                                if (player.isVideo) {
                                        player.showControls();
                                        player.startControlsTimer();
                                }
                                if (player.media.muted) {
                                        player.setMuted(false);
                                } else {
                                        player.setMuted(true);
                                }
                        }
                }
        ]
    };

    mejs.mepIndex = 0;

    mejs.players = {};

    // wraps a MediaElement object in player controls
    mejs.MediaElementPlayer = function(node, o) {
        // enforce object, even without "new" (via John Resig)
        if ( !(this instanceof mejs.MediaElementPlayer) ) {
            return new mejs.MediaElementPlayer(node, o);
        }

        var t = this;

        // these will be reset after the MediaElement.success fires
        t.$media = t.$node = $(node);
        t.node = t.media = t.$media[0];

        if(!t.node) {
            return
        }

        // check for existing player
        if (typeof t.node.player != 'undefined') {
            return t.node.player;
        }


        // try to get options from data-mejsoptions
        if (typeof o == 'undefined') {
            o = t.$node.data('mejsoptions');
        }

        // extend default options
        t.options = $.extend({},mejs.MepDefaults,o);

        if (!t.options.timeFormat) {
            // Generate the time format according to options
            t.options.timeFormat = 'mm:ss';
            if (t.options.alwaysShowHours) {
                t.options.timeFormat = 'hh:mm:ss';
            }
            if (t.options.showTimecodeFrameCount) {
                t.options.timeFormat += ':ff';
            }
        }

        mejs.Utility.calculateTimeFormat(0, t.options, t.options.framesPerSecond || 25);

        // unique ID
        t.id = 'mep_' + mejs.mepIndex++;

        // add to player array (for focus events)
        mejs.players[t.id] = t;

        // start up
        t.init();

        return t;
    };

    // actual player
    mejs.MediaElementPlayer.prototype = {

        hasFocus: false,

        controlsAreVisible: true,

        init: function() {

            var
                t = this,
                mf = mejs.MediaFeatures,
                // options for MediaElement (shim)
                meOptions = $.extend(true, {}, t.options, {
                    success: function(media, domNode) { t.meReady(media, domNode); },
                    error: function(e) { t.handleError(e);}
                }),
                tagName = t.media.tagName.toLowerCase();

            t.isDynamic = (tagName !== 'audio' && tagName !== 'video');

            if (t.isDynamic) {
                // get video from src or href?
                t.isVideo = t.options.isVideo;
            } else {
                t.isVideo = (tagName !== 'audio' && t.options.isVideo);
            }

            // use native controls in iPad, iPhone, and Android
            if ((mf.isiPad && t.options.iPadUseNativeControls) || (mf.isiPhone && t.options.iPhoneUseNativeControls)) {

                // add controls and stop
                t.$media.attr('controls', 'controls');

                // attempt to fix iOS 3 bug
                //t.$media.removeAttr('poster');
                                // no Issue found on iOS3 -ttroxell

                // override Apple's autoplay override for iPads
                if (mf.isiPad && t.media.getAttribute('autoplay') !== null) {
                    t.play();
                }

            } else if (mf.isAndroid && t.options.AndroidUseNativeControls) {

                // leave default player

            } else {

                // DESKTOP: use MediaElementPlayer controls

                // remove native controls
                t.$media.removeAttr('controls');
                var videoPlayerTitle = t.isVideo ?
                    mejs.i18n.t('Video Player') : mejs.i18n.t('Audio Player');
                // insert description for screen readers
                $('<span class="mejs-offscreen">' + videoPlayerTitle + '</span>').insertBefore(t.$media);
                // build container
                t.container =
                    $('<div id="' + t.id + '" class="mejs-container ' + (mejs.MediaFeatures.svg ? 'svg' : 'no-svg') +
                      '" tabindex="0" role="application" aria-label="' + videoPlayerTitle + '">'+
                        '<div class="mejs-inner">'+
                            '<div class="mejs-mediaelement"></div>'+
                            '<div class="mejs-layers"></div>'+
                            '<div class="mejs-controls"></div>'+
                            '<div class="mejs-clear"></div>'+
                        '</div>' +
                    '</div>')
                    .addClass(t.$media[0].className)
                    .insertBefore(t.$media)
                    .focus(function ( e ) {
                        if( !t.controlsAreVisible ) {
                            t.showControls(true);
                            var playButton = t.container.find('.mejs-playpause-button > button');
                            playButton.focus();
                        }
                    });

                // add classes for user and content
                t.container.addClass(
                    (mf.isAndroid ? 'mejs-android ' : '') +
                    (mf.isiOS ? 'mejs-ios ' : '') +
                    (mf.isiPad ? 'mejs-ipad ' : '') +
                    (mf.isiPhone ? 'mejs-iphone ' : '') +
                    (t.isVideo ? 'mejs-video ' : 'mejs-audio ')
                );


                // move the <video/video> tag into the right spot
                if (mf.isiOS) {

                    // sadly, you can't move nodes in iOS, so we have to destroy and recreate it!
                    var $newMedia = t.$media.clone();

                    t.container.find('.mejs-mediaelement').append($newMedia);

                    t.$media.remove();
                    t.$node = t.$media = $newMedia;
                    t.node = t.media = $newMedia[0];

                } else {

                    // normal way of moving it into place (doesn't work on iOS)
                    t.container.find('.mejs-mediaelement').append(t.$media);
                }
                
                // needs to be assigned here, after iOS remap
                t.node.player = t;

                // find parts
                t.controls = t.container.find('.mejs-controls');
                t.layers = t.container.find('.mejs-layers');

                // determine the size

                /* size priority:
                    (1) videoWidth (forced),
                    (2) style="width;height;"
                    (3) width attribute,
                    (4) defaultVideoWidth (for unspecified cases)
                */

                var tagType = (t.isVideo ? 'video' : 'audio'),
                    capsTagName = tagType.substring(0,1).toUpperCase() + tagType.substring(1);



                if (t.options[tagType + 'Width'] > 0 || t.options[tagType + 'Width'].toString().indexOf('%') > -1) {
                    t.width = t.options[tagType + 'Width'];
                } else if (t.media.style.width !== '' && t.media.style.width !== null) {
                    t.width = t.media.style.width;
                } else if (t.media.getAttribute('width') !== null) {
                    t.width = t.$media.attr('width');
                } else {
                    t.width = t.options['default' + capsTagName + 'Width'];
                }

                if (t.options[tagType + 'Height'] > 0 || t.options[tagType + 'Height'].toString().indexOf('%') > -1) {
                    t.height = t.options[tagType + 'Height'];
                } else if (t.media.style.height !== '' && t.media.style.height !== null) {
                    t.height = t.media.style.height;
                } else if (t.$media[0].getAttribute('height') !== null) {
                    t.height = t.$media.attr('height');
                } else {
                    t.height = t.options['default' + capsTagName + 'Height'];
                }

                // set the size, while we wait for the plugins to load below
                t.setPlayerSize(t.width, t.height);

                // create MediaElementShim
                meOptions.pluginWidth = t.width;
                meOptions.pluginHeight = t.height;
            }

            // create MediaElement shim
            mejs.MediaElement(t.$media[0], meOptions);

            if (typeof(t.container) != 'undefined' && t.controlsAreVisible){
                // controls are shown when loaded
                t.container.trigger('controlsshown');
            }
        },

        showControls: function(doAnimation) {
            var t = this;

            doAnimation = typeof doAnimation == 'undefined' || doAnimation;

            if (t.controlsAreVisible)
                return;

            if (doAnimation) {
                t.controls
                    .css('visibility','visible')
                    .stop(true, true).fadeIn(200, function() {
                        t.controlsAreVisible = true;
                        t.container.trigger('controlsshown');
                    });

                // any additional controls people might add and want to hide
                t.container.find('.mejs-control')
                    .css('visibility','visible')
                    .stop(true, true).fadeIn(200, function() {t.controlsAreVisible = true;});

            } else {
                t.controls
                    .css('visibility','visible')
                    .css('display','block');

                // any additional controls people might add and want to hide
                t.container.find('.mejs-control')
                    .css('visibility','visible')
                    .css('display','block');

                t.controlsAreVisible = true;
                t.container.trigger('controlsshown');
            }

            t.setControlsSize();

        },

        hideControls: function(doAnimation) {
            var t = this;

            doAnimation = typeof doAnimation == 'undefined' || doAnimation;

            if (!t.controlsAreVisible || t.options.alwaysShowControls || t.keyboardAction)
                return;

            if (doAnimation) {
                // fade out main controls
                t.controls.stop(true, true).fadeOut(200, function() {
                    $(this)
                        .css('visibility','hidden')
                        .css('display','block');

                    t.controlsAreVisible = false;
                    t.container.trigger('controlshidden');
                });

                // any additional controls people might add and want to hide
                t.container.find('.mejs-control').stop(true, true).fadeOut(200, function() {
                    $(this)
                        .css('visibility','hidden')
                        .css('display','block');
                });
            } else {

                // hide main controls
                t.controls
                    .css('visibility','hidden')
                    .css('display','block');

                // hide others
                t.container.find('.mejs-control')
                    .css('visibility','hidden')
                    .css('display','block');

                t.controlsAreVisible = false;
                t.container.trigger('controlshidden');
            }
        },

        controlsTimer: null,

        startControlsTimer: function(timeout) {

            var t = this;

            timeout = typeof timeout != 'undefined' ? timeout : 1500;

            t.killControlsTimer('start');

            t.controlsTimer = setTimeout(function() {
                //
                t.hideControls();
                t.killControlsTimer('hide');
            }, timeout);
        },

        killControlsTimer: function(src) {

            var t = this;

            if (t.controlsTimer !== null) {
                clearTimeout(t.controlsTimer);
                delete t.controlsTimer;
                t.controlsTimer = null;
            }
        },

        controlsEnabled: true,

        disableControls: function() {
            var t= this;

            t.killControlsTimer();
            t.hideControls(false);
            this.controlsEnabled = false;
        },

        enableControls: function() {
            var t= this;

            t.showControls(false);

            t.controlsEnabled = true;
        },


        // Sets up all controls and events
        meReady: function(media, domNode) {


            var t = this,
                mf = mejs.MediaFeatures,
                autoplayAttr = domNode.getAttribute('autoplay'),
                autoplay = !(typeof autoplayAttr == 'undefined' || autoplayAttr === null || autoplayAttr === 'false'),
                featureIndex,
                feature;

            // make sure it can't create itself again if a plugin reloads
            if (t.created) {
                return;
            } else {
                t.created = true;
            }

            t.media = media;
            t.domNode = domNode;

            if (!(mf.isAndroid && t.options.AndroidUseNativeControls) && !(mf.isiPad && t.options.iPadUseNativeControls) && !(mf.isiPhone && t.options.iPhoneUseNativeControls)) {

                // two built in features
                t.buildposter(t, t.controls, t.layers, t.media);
                t.buildkeyboard(t, t.controls, t.layers, t.media);
                t.buildoverlays(t, t.controls, t.layers, t.media);

                // grab for use by features
                t.findTracks();

                // add user-defined features/controls
                for (featureIndex in t.options.features) {
                    feature = t.options.features[featureIndex];
                    if (t['build' + feature]) {
                        try {
                            t['build' + feature](t, t.controls, t.layers, t.media);
                        } catch (e) {
                            // TODO: report control error
                            //throw e;
                            
                            
                        }
                    }
                }

                t.container.trigger('controlsready');

                // reset all layers and controls
                t.setPlayerSize(t.width, t.height);
                t.setControlsSize();


                // controls fade
                if (t.isVideo) {

                    if (mejs.MediaFeatures.hasTouch) {

                        // for touch devices (iOS, Android)
                        // show/hide without animation on touch

                        t.$media.bind('touchstart', function() {


                            // toggle controls
                            if (t.controlsAreVisible) {
                                t.hideControls(false);
                            } else {
                                if (t.controlsEnabled) {
                                    t.showControls(false);
                                }
                            }
                        });

                    } else {

                        // create callback here since it needs access to current
                        // MediaElement object
                        t.clickToPlayPauseCallback = function() {
                            //

                            if (t.options.clickToPlayPause) {
                                if (t.media.paused) {
                                    t.play();
                                } else {
                                    t.pause();
                                }
                            }
                        };

                        // click to play/pause
                        t.media.addEventListener('click', t.clickToPlayPauseCallback, false);

                        // show/hide controls
                        t.container
                            .bind('mouseenter mouseover', function () {
                                if (t.controlsEnabled) {
                                    if (!t.options.alwaysShowControls ) {
                                        t.killControlsTimer('enter');
                                        t.showControls();
                                        t.startControlsTimer(2500);
                                    }
                                }
                            })
                            .bind('mousemove', function() {
                                if (t.controlsEnabled) {
                                    if (!t.controlsAreVisible) {
                                        t.showControls();
                                    }
                                    if (!t.options.alwaysShowControls) {
                                        t.startControlsTimer(2500);
                                    }
                                }
                            })
                            .bind('mouseleave', function () {
                                if (t.controlsEnabled) {
                                    if (!t.media.paused && !t.options.alwaysShowControls) {
                                        t.startControlsTimer(1000);
                                    }
                                }
                            });
                    }

                    if(t.options.hideVideoControlsOnLoad) {
                        t.hideControls(false);
                    }

                    // check for autoplay
                    if (autoplay && !t.options.alwaysShowControls) {
                        t.hideControls();
                    }

                    // resizer
                    if (t.options.enableAutosize) {
                        t.media.addEventListener('loadedmetadata', function(e) {
                            // if the <video height> was not set and the options.videoHeight was not set
                            // then resize to the real dimensions
                            if (t.options.videoHeight <= 0 && t.domNode.getAttribute('height') === null && !isNaN(e.target.videoHeight)) {
                                t.setPlayerSize(e.target.videoWidth, e.target.videoHeight);
                                t.setControlsSize();
                                t.media.setVideoSize(e.target.videoWidth, e.target.videoHeight);
                            }
                        }, false);
                    }
                }

                // EVENTS

                // FOCUS: when a video starts playing, it takes focus from other players (possibily pausing them)
                media.addEventListener('play', function() {
                    var playerIndex;

                    // go through all other players
                    for (playerIndex in mejs.players) {
                        var p = mejs.players[playerIndex];
                        if (p.id != t.id && t.options.pauseOtherPlayers && !p.paused && !p.ended) {
                            p.pause();
                        }
                        p.hasFocus = false;
                    }

                    t.hasFocus = true;
                },false);


                // ended for all
                t.media.addEventListener('ended', function (e) {
                    if(t.options.autoRewind) {
                        try{
                            t.media.setCurrentTime(0);
                            // Fixing an Android stock browser bug, where "seeked" isn't fired correctly after ending the video and jumping to the beginning
                            window.setTimeout(function(){
                                $(t.container).find('.mejs-overlay-loading').parent().hide();
                            }, 20);
                        } catch (exp) {

                        }
                    }
                    t.media.pause();

                    if (t.setProgressRail) {
                        t.setProgressRail();
                    }
                    if (t.setCurrentRail) {
                        t.setCurrentRail();
                    }

                    if (t.options.loop) {
                        t.play();
                    } else if (!t.options.alwaysShowControls && t.controlsEnabled) {
                        t.showControls();
                    }
                }, false);

                // resize on the first play
                t.media.addEventListener('loadedmetadata', function(e) {
                    if (t.updateDuration) {
                        t.updateDuration();
                    }
                    if (t.updateCurrent) {
                        t.updateCurrent();
                    }

                    if (!t.isFullScreen) {
                        t.setPlayerSize(t.width, t.height);
                        t.setControlsSize();
                    }
                }, false);
                
                // Only change the time format when necessary
                var duration = null;
                t.media.addEventListener('timeupdate',function() {
                    if (duration !== this.duration) {
                        duration = this.duration;
                        mejs.Utility.calculateTimeFormat(duration, t.options, t.options.framesPerSecond || 25);
                    }
                }, false);              

                t.container.focusout(function (e) {
                    if( e.relatedTarget ) { //FF is working on supporting focusout https://bugzilla.mozilla.org/show_bug.cgi?id=687787
                        var $target = $(e.relatedTarget);
                        if (t.keyboardAction && $target.parents('.mejs-container').length === 0) {
                            t.keyboardAction = false;
                            t.hideControls(true);
                        }
                    }
                });

                // webkit has trouble doing this without a delay
                setTimeout(function () {
                    t.setPlayerSize(t.width, t.height);
                    t.setControlsSize();
                }, 50);

                // adjust controls whenever window sizes (used to be in fullscreen only)
                t.globalBind('resize', function() {

                    // don't resize for fullscreen mode
                    if ( !(t.isFullScreen || (mejs.MediaFeatures.hasTrueNativeFullScreen && document.webkitIsFullScreen)) ) {
                        t.setPlayerSize(t.width, t.height);
                    }

                    // always adjust controls
                    t.setControlsSize();
                });

                // This is a work-around for a bug in the YouTube iFrame player, which means
                //  we can't use the play() API for the initial playback on iOS or Android;
                //  user has to start playback directly by tapping on the iFrame.
                if (t.media.pluginType == 'youtube' && ( mf.isiOS || mf.isAndroid ) ) {
                    t.container.find('.mejs-overlay-play').hide();
                }
            }

            // force autoplay for HTML5
            if (autoplay && media.pluginType == 'native') {
                t.play();
            }


            if (t.options.success) {

                if (typeof t.options.success == 'string') {
                    window[t.options.success](t.media, t.domNode, t);
                } else {
                    t.options.success(t.media, t.domNode, t);
                }
            }
        },

        handleError: function(e) {
            var t = this;

            t.controls.hide();

            // Tell user that the file cannot be played
            if (t.options.error) {
                t.options.error(e);
            }
        },

        setPlayerSize: function(width,height) {
            var t = this;

            if( !t.options.setDimensions ) {
                return false;
            }

            if (typeof width != 'undefined') {
                t.width = width;
            }

            if (typeof height != 'undefined') {
                t.height = height;
            }

            // detect 100% mode - use currentStyle for IE since css() doesn't return percentages
            if (t.height.toString().indexOf('%') > 0 || (t.$node.css('max-width') !== 'none' && t.$node.css('max-width') !== 't.width') || (t.$node[0].currentStyle && t.$node[0].currentStyle.maxWidth === '100%')) {

                // do we have the native dimensions yet?
                var nativeWidth = (function() {
                    if (t.isVideo) {
                        if (t.media.videoWidth && t.media.videoWidth > 0) {
                            return t.media.videoWidth;
                        } else if (t.media.getAttribute('width') !== null) {
                            return t.media.getAttribute('width');
                        } else {
                            return t.options.defaultVideoWidth;
                        }
                    } else {
                        return t.options.defaultAudioWidth;
                    }
                })();

                var nativeHeight = (function() {
                    if (t.isVideo) {
                        if (t.media.videoHeight && t.media.videoHeight > 0) {
                            return t.media.videoHeight;
                        } else if (t.media.getAttribute('height') !== null) {
                            return t.media.getAttribute('height');
                        } else {
                            return t.options.defaultVideoHeight;
                        }
                    } else {
                        return t.options.defaultAudioHeight;
                    }
                })();

                var
                    parentWidth = t.container.parent().closest(':visible').width(),
                    parentHeight = t.container.parent().closest(':visible').height(),
                    newHeight = t.isVideo || !t.options.autosizeProgress ? parseInt(parentWidth * nativeHeight/nativeWidth, 10) : nativeHeight;

                // When we use percent, the newHeight can't be calculated so we get the container height
                if (isNaN(newHeight)) {
                    newHeight = parentHeight;
                }

                if (t.container.parent().length > 0 && t.container.parent()[0].tagName.toLowerCase() === 'body') { // && t.container.siblings().count == 0) {
                    parentWidth = $(window).width();
                    newHeight = $(window).height();
                }

                if ( newHeight && parentWidth ) {

                    // set outer container size
                    t.container
                        .width(parentWidth)
                        .height(newHeight);

                    // set native <video> or <audio> and shims
                    t.$media.add(t.container.find('.mejs-shim'))
                        .width('100%')
                        .height('100%');

                    // if shim is ready, send the size to the embeded plugin
                    if (t.isVideo) {
                        if (t.media.setVideoSize) {
                            t.media.setVideoSize(parentWidth, newHeight);
                        }
                    }

                    // set the layers
                    t.layers.children('.mejs-layer')
                        .width('100%')
                        .height('100%');
                }


            } else {

                t.container
                    .width(t.width)
                    .height(t.height);

                t.layers.children('.mejs-layer')
                    .width(t.width)
                    .height(t.height);

            }

        },

        setControlsSize: function() {
            var t = this,
                usedWidth = 0,
                railWidth = 0,
                rail = t.controls.find('.mejs-time-rail'),
                total = t.controls.find('.mejs-time-total'),
                others = rail.siblings(),
                lastControl = others.last(),
                lastControlPosition = null;

            // skip calculation if hidden
            if (!t.container.is(':visible') || !rail.length || !rail.is(':visible')) {
                return;
            }


            // allow the size to come from custom CSS
            if (t.options && !t.options.autosizeProgress) {
                // Also, frontends devs can be more flexible
                // due the opportunity of absolute positioning.
                railWidth = parseInt(rail.css('width'), 10);
            }

            // attempt to autosize
            if (railWidth === 0 || !railWidth) {

                // find the size of all the other controls besides the rail
                others.each(function() {
                    var $this = $(this);
                    if ($this.css('position') != 'absolute' && $this.is(':visible')) {
                        usedWidth += $(this).outerWidth(true);
                    }
                });

                // fit the rail into the remaining space
                railWidth = t.controls.width() - usedWidth - (rail.outerWidth(true) - rail.width());
            }

            // resize the rail,
            // but then check if the last control (say, the fullscreen button) got pushed down
            // this often happens when zoomed
            do {
                // outer area
                rail.width(railWidth);
                // dark space
                total.width(railWidth - (total.outerWidth(true) - total.width()));

                if (lastControl.css('position') != 'absolute') {
                    lastControlPosition = lastControl.length ? lastControl.position() : null;
                    railWidth--;
                }
            } while (lastControlPosition !== null && lastControlPosition.top > 0 && railWidth > 0);

            t.container.trigger('controlsresize');
        },


        buildposter: function(player, controls, layers, media) {
            var t = this,
                poster =
                $('<div class="mejs-poster mejs-layer">' +
                '</div>')
                    .appendTo(layers),
                posterUrl = player.$media.attr('poster');

            // prioriy goes to option (this is useful if you need to support iOS 3.x (iOS completely fails with poster)
            if (player.options.poster !== '') {
                posterUrl = player.options.poster;
            }

            // second, try the real poster
            if ( posterUrl ) {
                t.setPoster(posterUrl);
            } else {
                poster.hide();
            }

            media.addEventListener('play',function() {
                poster.hide();
            }, false);

            if(player.options.showPosterWhenEnded && player.options.autoRewind){
                media.addEventListener('ended',function() {
                    poster.show();
                }, false);
            }
        },

        setPoster: function(url) {
            var t = this,
                posterDiv = t.container.find('.mejs-poster'),
                posterImg = posterDiv.find('img');

            if (posterImg.length === 0) {
                posterImg = $('<img width="100%" height="100%" alt="" />').appendTo(posterDiv);
            }

            posterImg.attr('src', url);
            posterDiv.css({'background-image' : 'url(' + url + ')'});
        },

        buildoverlays: function(player, controls, layers, media) {
            var t = this;
            if (!player.isVideo)
                return;

            var
            loading =
                $('<div class="mejs-overlay mejs-layer">'+
                    '<div class="mejs-overlay-loading"><span></span></div>'+
                '</div>')
                .hide() // start out hidden
                .appendTo(layers),
            error =
                $('<div class="mejs-overlay mejs-layer">'+
                    '<div class="mejs-overlay-error"></div>'+
                '</div>')
                .hide() // start out hidden
                .appendTo(layers),
            // this needs to come last so it's on top
            bigPlay =
                $('<div class="mejs-overlay mejs-layer mejs-overlay-play">'+
                    '<div class="mejs-overlay-button"></div>'+
                '</div>')
                .appendTo(layers)
                .bind('click', function() {  // Removed 'touchstart' due issues on Samsung Android devices where a tap on bigPlay started and immediately stopped the video
                    if (t.options.clickToPlayPause) {
                        if (media.paused) {
                            media.play();
                        }
                    }
                });

            /*
            if (mejs.MediaFeatures.isiOS || mejs.MediaFeatures.isAndroid) {
                bigPlay.remove();
                loading.remove();
            }
            */


            // show/hide big play button
            media.addEventListener('play',function() {
                bigPlay.hide();
                loading.hide();
                controls.find('.mejs-time-buffering').hide();
                error.hide();
            }, false);

            media.addEventListener('playing', function() {
                bigPlay.hide();
                loading.hide();
                controls.find('.mejs-time-buffering').hide();
                error.hide();
            }, false);

            media.addEventListener('seeking', function() {
                loading.show();
                controls.find('.mejs-time-buffering').show();
            }, false);

            media.addEventListener('seeked', function() {
                loading.hide();
                controls.find('.mejs-time-buffering').hide();
            }, false);

            media.addEventListener('pause',function() {
                if (!mejs.MediaFeatures.isiPhone) {
                    bigPlay.show();
                }
            }, false);

            media.addEventListener('waiting', function() {
                loading.show();
                controls.find('.mejs-time-buffering').show();
            }, false);


            // show/hide loading
            media.addEventListener('loadeddata',function() {
                // for some reason Chrome is firing this event
                //if (mejs.MediaFeatures.isChrome && media.getAttribute && media.getAttribute('preload') === 'none')
                //  return;

                loading.show();
                controls.find('.mejs-time-buffering').show();
                // Firing the 'canplay' event after a timeout which isn't getting fired on some Android 4.1 devices (https://github.com/johndyer/mediaelement/issues/1305)
                if (mejs.MediaFeatures.isAndroid) {
                    media.canplayTimeout = window.setTimeout(
                        function() {
                            if (document.createEvent) {
                                var evt = document.createEvent('HTMLEvents');
                                evt.initEvent('canplay', true, true);
                                return media.dispatchEvent(evt);
                            }
                        }, 300
                    );
                }
            }, false);
            media.addEventListener('canplay',function() {
                loading.hide();
                controls.find('.mejs-time-buffering').hide();
                clearTimeout(media.canplayTimeout); // Clear timeout inside 'loadeddata' to prevent 'canplay' to fire twice
            }, false);

            // error handling
            media.addEventListener('error',function(e) {
                t.handleError(e);
                loading.hide();
                bigPlay.hide();
                error.show();
                error.find('.mejs-overlay-error').html("Error loading this resource");
            }, false);

            media.addEventListener('keydown', function(e) {
                t.onkeydown(player, media, e);
            }, false);
        },

        buildkeyboard: function(player, controls, layers, media) {

                var t = this;

                t.container.keydown(function () {
                    t.keyboardAction = true;
                });

                // listen for key presses
                t.globalBind('keydown', function(e) {
                    return t.onkeydown(player, media, e);
                });


                // check if someone clicked outside a player region, then kill its focus
                t.globalBind('click', function(event) {
                    player.hasFocus = $(event.target).closest('.mejs-container').length !== 0;
                });

        },
        onkeydown: function(player, media, e) {
            if (player.hasFocus && player.options.enableKeyboard) {
                // find a matching key
                for (var i = 0, il = player.options.keyActions.length; i < il; i++) {
                    var keyAction = player.options.keyActions[i];

                    for (var j = 0, jl = keyAction.keys.length; j < jl; j++) {
                        if (e.keyCode == keyAction.keys[j]) {
                            if (typeof(e.preventDefault) == "function") e.preventDefault();
                            keyAction.action(player, media, e.keyCode);
                            return false;
                        }
                    }
                }
            }

            return true;
        },

        findTracks: function() {
            var t = this,
                tracktags = t.$media.find('track');

            // store for use by plugins
            t.tracks = [];
            tracktags.each(function(index, track) {

                track = $(track);

                t.tracks.push({
                    srclang: (track.attr('srclang')) ? track.attr('srclang').toLowerCase() : '',
                    src: track.attr('src'),
                    kind: track.attr('kind'),
                    label: track.attr('label') || '',
                    entries: [],
                    isLoaded: false
                });
            });
        },
        changeSkin: function(className) {
            this.container[0].className = 'mejs-container ' + className;
            this.setPlayerSize(this.width, this.height);
            this.setControlsSize();
        },
        play: function() {
            this.load();
            this.media.play();
        },
        pause: function() {
            try {
                this.media.pause();
            } catch (e) {}
        },
        load: function() {
            if (!this.isLoaded) {
                this.media.load();
            }

            this.isLoaded = true;
        },
        setMuted: function(muted) {
            this.media.setMuted(muted);
        },
        setCurrentTime: function(time) {
            this.media.setCurrentTime(time);
        },
        getCurrentTime: function() {
            return this.media.currentTime;
        },
        setVolume: function(volume) {
            this.media.setVolume(volume);
        },
        getVolume: function() {
            return this.media.volume;
        },
        setSrc: function(src) {
            this.media.setSrc(src);
        },
        remove: function() {
            var t = this, featureIndex, feature;
            
            t.container.prev('.mejs-offscreen').remove();

            // invoke features cleanup
            for (featureIndex in t.options.features) {
                feature = t.options.features[featureIndex];
                if (t['clean' + feature]) {
                    try {
                        t['clean' + feature](t);
                    } catch (e) {
                        // TODO: report control error
                        //throw e;
                        //
                        //
                    }
                }
            }

            // grab video and put it back in place
            if (!t.isDynamic) {
                t.$media.prop('controls', true);
                // detach events from the video
                // TODO: detach event listeners better than this;
                //       also detach ONLY the events attached by this plugin!
                t.$node.clone().insertBefore(t.container).show();
                t.$node.remove();
            } else {
                t.$node.insertBefore(t.container);
            }

            if (t.media.pluginType !== 'native') {
                t.media.remove();
            }

            // Remove the player from the mejs.players object so that pauseOtherPlayers doesn't blow up when trying to pause a non existance flash api.
            delete mejs.players[t.id];

            if (typeof t.container == 'object') {
                t.container.remove();
            }
            t.globalUnbind();
            delete t.node.player;
        },
        rebuildtracks: function(){
            var t = this;
            t.findTracks();
            t.buildtracks(t, t.controls, t.layers, t.media);
        },
        resetSize: function(){
            var t = this;
            // webkit has trouble doing this without a delay
            setTimeout(function () {
                //
                t.setPlayerSize(t.width, t.height);
                t.setControlsSize();
            }, 50);            
        }
    };

    (function(){
        var rwindow = /^((after|before)print|(before)?unload|hashchange|message|o(ff|n)line|page(hide|show)|popstate|resize|storage)\b/;

        function splitEvents(events, id) {
            // add player ID as an event namespace so it's easier to unbind them all later
            var ret = {d: [], w: []};
            $.each((events || '').split(' '), function(k, v){
                var eventname = v + '.' + id;
                if (eventname.indexOf('.') === 0) {
                    ret.d.push(eventname);
                    ret.w.push(eventname);
                }
                else {
                    ret[rwindow.test(v) ? 'w' : 'd'].push(eventname);
                }
            });
            ret.d = ret.d.join(' ');
            ret.w = ret.w.join(' ');
            return ret;
        }

        mejs.MediaElementPlayer.prototype.globalBind = function(events, data, callback) {
            var t = this;
            events = splitEvents(events, t.id);
            if (events.d) $(document).bind(events.d, data, callback);
            if (events.w) $(window).bind(events.w, data, callback);
        };

        mejs.MediaElementPlayer.prototype.globalUnbind = function(events, callback) {
            var t = this;
            events = splitEvents(events, t.id);
            if (events.d) $(document).unbind(events.d, callback);
            if (events.w) $(window).unbind(events.w, callback);
        };
    })();

    // turn into jQuery plugin
    if (typeof $ != 'undefined') {
        $.fn.mediaelementplayer = function (options) {
            if (options === false) {
                this.each(function () {
                    var player = $(this).data('mediaelementplayer');
                    if (player) {
                        player.remove();
                    }
                    $(this).removeData('mediaelementplayer');
                });
            }
            else {
                this.each(function () {
                    $(this).data('mediaelementplayer', new mejs.MediaElementPlayer(this, options));
                });
            }
            return this;
        };


        $(document).ready(function() {
            // auto enable using JSON attribute
            $('.mejs-player').mediaelementplayer();
        });
    }

    // push out to window
    window.MediaElementPlayer = mejs.MediaElementPlayer;

})(mejs.$);

(function($) {

    $.extend(mejs.MepDefaults, {
        playText: mejs.i18n.t('Play'),
        pauseText: mejs.i18n.t('Pause')
    });

    // PLAY/pause BUTTON
    $.extend(MediaElementPlayer.prototype, {
        buildplaypause: function(player, controls, layers, media) {
            var 
                t = this,
                op = t.options,
                play = 
                $('<div class="mejs-button mejs-playpause-button mejs-play" >' +
                    '<button type="button" aria-controls="' + t.id + '" title="' + op.playText + '" aria-label="' + op.playText + '"></button>' +
                '</div>')
                .appendTo(controls)
                .click(function(e) {
                    e.preventDefault();
                
                    if (media.paused) {
                        media.play();
                    } else {
                        media.pause();
                    }
                    
                    return false;
                }),
                play_btn = play.find('button');


            function togglePlayPause(which) {
                if ('play' === which) {
                    play.removeClass('mejs-play').addClass('mejs-pause');
                    play_btn.attr({
                        'title': op.pauseText,
                        'aria-label': op.pauseText
                    });
                } else {
                    play.removeClass('mejs-pause').addClass('mejs-play');
                    play_btn.attr({
                        'title': op.playText,
                        'aria-label': op.playText
                    });
                }
            };
            togglePlayPause('pse');


            media.addEventListener('play',function() {
                togglePlayPause('play');
            }, false);
            media.addEventListener('playing',function() {
                togglePlayPause('play');
            }, false);


            media.addEventListener('pause',function() {
                togglePlayPause('pse');
            }, false);
            media.addEventListener('paused',function() {
                togglePlayPause('pse');
            }, false);
        }
    });
    
})(mejs.$);

(function($) {

    $.extend(mejs.MepDefaults, {
        stopText: 'Stop'
    });

    // STOP BUTTON
    $.extend(MediaElementPlayer.prototype, {
        buildstop: function(player, controls, layers, media) {
            var t = this;

            $('<div class="mejs-button mejs-stop-button mejs-stop">' +
                    '<button type="button" aria-controls="' + t.id + '" title="' + t.options.stopText + '" aria-label="' + t.options.stopText + '"></button>' +
                '</div>')
                .appendTo(controls)
                .click(function() {
                    if (!media.paused) {
                        media.pause();
                    }
                    if (media.currentTime > 0) {
                        media.setCurrentTime(0);
                        media.pause();
                        controls.find('.mejs-time-current').width('0px');
                        controls.find('.mejs-time-handle').css('left', '0px');
                        controls.find('.mejs-time-float-current').html( mejs.Utility.secondsToTimeCode(0, player.options));
                        controls.find('.mejs-currenttime').html( mejs.Utility.secondsToTimeCode(0, player.options));
                        layers.find('.mejs-poster').show();
                    }
                });
        }
    });
    
})(mejs.$);

(function($) {

    $.extend(mejs.MepDefaults, {
        progessHelpText: mejs.i18n.t(
        'Use Left/Right Arrow keys to advance one second, Up/Down arrows to advance ten seconds.')
    });

    // progress/loaded bar
    $.extend(MediaElementPlayer.prototype, {
        buildprogress: function(player, controls, layers, media) {

            $('<div class="mejs-time-rail">' +
                '<span  class="mejs-time-total mejs-time-slider">' +
                //'<span class="mejs-offscreen">' + this.options.progessHelpText + '</span>' +
                    '<span class="mejs-time-buffering"></span>' +
                    '<span class="mejs-time-loaded"></span>' +
                    '<span class="mejs-time-current"></span>' +
                    '<span class="mejs-time-handle"></span>' +
                    '<span class="mejs-time-float">' +
                        '<span class="mejs-time-float-current">00:00</span>' +
                        '<span class="mejs-time-float-corner"></span>' +
                    '</span>' +
                '</span>' +
            '</div>')
                .appendTo(controls);
            controls.find('.mejs-time-buffering').hide();

            var 
                t = this,
                total = controls.find('.mejs-time-total'),
                loaded  = controls.find('.mejs-time-loaded'),
                current  = controls.find('.mejs-time-current'),
                handle  = controls.find('.mejs-time-handle'),
                timefloat  = controls.find('.mejs-time-float'),
                timefloatcurrent  = controls.find('.mejs-time-float-current'),
                slider = controls.find('.mejs-time-slider'),
                handleMouseMove = function (e) {
                    
                    var offset = total.offset(),
                        width = total.width(),
                        percentage = 0,
                        newTime = 0,
                        pos = 0,
                        x;
                    
                    // mouse or touch position relative to the object
                    if (e.originalEvent && e.originalEvent.changedTouches) {
                        x = e.originalEvent.changedTouches[0].pageX;
                    } else if (e.changedTouches) { // for Zepto
                        x = e.changedTouches[0].pageX;
                    } else {
                        x = e.pageX;
                    }

                    if (media.duration) {
                        if (x < offset.left) {
                            x = offset.left;
                        } else if (x > width + offset.left) {
                            x = width + offset.left;
                        }
                        
                        pos = x - offset.left;
                        percentage = (pos / width);
                        newTime = (percentage <= 0.02) ? 0 : percentage * media.duration;

                        // seek to where the mouse is
                        if (mouseIsDown && newTime !== media.currentTime) {
                            media.setCurrentTime(newTime);
                        }

                        // position floating time box
                        if (!mejs.MediaFeatures.hasTouch) {
                                timefloat.css('left', pos);
                                timefloatcurrent.html( mejs.Utility.secondsToTimeCode(newTime, player.options) );
                                timefloat.show();
                        }
                    }
                },
                mouseIsDown = false,
                mouseIsOver = false,
                lastKeyPressTime = 0,
                startedPaused = false,
                autoRewindInitial = player.options.autoRewind;
            // Accessibility for slider
            var updateSlider = function (e) {

                var seconds = media.currentTime,
                    timeSliderText = mejs.i18n.t('Time Slider'),
                    time = mejs.Utility.secondsToTimeCode(seconds, player.options),
                    duration = media.duration;

                slider.attr({
                    'aria-label': timeSliderText,
                    'aria-valuemin': 0,
                    'aria-valuemax': duration,
                    'aria-valuenow': seconds,
                    'aria-valuetext': time,
                    'role': 'slider',
                    'tabindex': 0
                });

            };
            
            var restartPlayer = function () {
                var now = new Date();
                if (now - lastKeyPressTime >= 1000) {
                    media.play();
                }
            };

            slider.bind('focus', function (e) {
                player.options.autoRewind = false;
            });

            slider.bind('blur', function (e) {
                player.options.autoRewind = autoRewindInitial;
            });

            slider.bind('keydown', function (e) {

                if ((new Date() - lastKeyPressTime) >= 1000) {
                    startedPaused = media.paused;
                }

                var keyCode = e.keyCode,
                    duration = media.duration,
                    seekTime = media.currentTime;

                switch (keyCode) {
                case 37: // left
                    seekTime -= 1;
                    break;
                case 39: // Right
                    seekTime += 1;
                    break;
                case 38: // Up
                    seekTime += Math.floor(duration * 0.1);
                    break;
                case 40: // Down
                    seekTime -= Math.floor(duration * 0.1);
                    break;
                case 36: // Home
                    seekTime = 0;
                    break;
                case 35: // end
                    seekTime = duration;
                    break;
                case 10: // enter
                    media.paused ? media.play() : media.pause();
                    return;
                case 13: // space
                    media.paused ? media.play() : media.pause();
                    return;
                default:
                    return;
                }

                seekTime = seekTime < 0 ? 0 : (seekTime >= duration ? duration : Math.floor(seekTime));
                lastKeyPressTime = new Date();
                if (!startedPaused) {
                    media.pause();
                }

                if (seekTime < media.duration && !startedPaused) {
                    setTimeout(restartPlayer, 1100);
                }

                media.setCurrentTime(seekTime);

                e.preventDefault();
                e.stopPropagation();
                return false;
            });


            // handle clicks
            //controls.find('.mejs-time-rail').delegate('span', 'click', handleMouseMove);
            total
                .bind('mousedown touchstart', function (e) {
                    // only handle left clicks or touch
                    if (e.which === 1 || e.which === 0) {
                        mouseIsDown = true;
                        handleMouseMove(e);
                        t.globalBind('mousemove.dur touchmove.dur', function(e) {
                            handleMouseMove(e);
                        });
                        t.globalBind('mouseup.dur touchend.dur', function (e) {
                            mouseIsDown = false;
                            timefloat.hide();
                            t.globalUnbind('.dur');
                        });
                    }
                })
                .bind('mouseenter', function(e) {
                    mouseIsOver = true;
                    t.globalBind('mousemove.dur', function(e) {
                        handleMouseMove(e);
                    });
                    if (!mejs.MediaFeatures.hasTouch) {
                        timefloat.show();
                    }
                })
                .bind('mouseleave',function(e) {
                    mouseIsOver = false;
                    if (!mouseIsDown) {
                        t.globalUnbind('.dur');
                        timefloat.hide();
                    }
                });

            // loading
            media.addEventListener('progress', function (e) {
                player.setProgressRail(e);
                player.setCurrentRail(e);
            }, false);

            // current time
            media.addEventListener('timeupdate', function(e) {
                player.setProgressRail(e);
                player.setCurrentRail(e);
                updateSlider(e);
            }, false);
            
            t.container.on('controlsresize', function() {
                player.setProgressRail();
                player.setCurrentRail();
            });
            
            // store for later use
            t.loaded = loaded;
            t.total = total;
            t.current = current;
            t.handle = handle;
        },
        setProgressRail: function(e) {

            var
                t = this,
                target = (e !== undefined) ? e.target : t.media,
                percent = null;

            // newest HTML5 spec has buffered array (FF4, Webkit)
            if (target && target.buffered && target.buffered.length > 0 && target.buffered.end && target.duration) {
                // account for a real array with multiple values - always read the end of the last buffer
                percent = target.buffered.end(target.buffered.length - 1) / target.duration;
            } 
            // Some browsers (e.g., FF3.6 and Safari 5) cannot calculate target.bufferered.end()
            // to be anything other than 0. If the byte count is available we use this instead.
            // Browsers that support the else if do not seem to have the bufferedBytes value and
            // should skip to there. Tested in Safari 5, Webkit head, FF3.6, Chrome 6, IE 7/8.
            else if (target && target.bytesTotal !== undefined && target.bytesTotal > 0 && target.bufferedBytes !== undefined) {
                percent = target.bufferedBytes / target.bytesTotal;
            }
            // Firefox 3 with an Ogg file seems to go this way
            else if (e && e.lengthComputable && e.total !== 0) {
                percent = e.loaded / e.total;
            }

            // finally update the progress bar
            if (percent !== null) {
                percent = Math.min(1, Math.max(0, percent));
                // update loaded bar
                if (t.loaded && t.total) {
                    t.loaded.width(t.total.width() * percent);
                }
            }
        },
        setCurrentRail: function() {

            var t = this;
        
            if (t.media.currentTime !== undefined && t.media.duration) {

                // update bar and handle
                if (t.total && t.handle) {
                    var 
                        newWidth = Math.round(t.total.width() * t.media.currentTime / t.media.duration),
                        handlePos = newWidth - Math.round(t.handle.outerWidth(true) / 2);

                    t.current.width(newWidth);
                    t.handle.css('left', handlePos);
                }
            }

        }
    });
})(mejs.$);

(function($) {
    
    // options
    $.extend(mejs.MepDefaults, {
        duration: -1,
        timeAndDurationSeparator: '<span> | </span>'
    });


    // current and duration 00:00 / 00:00
    $.extend(MediaElementPlayer.prototype, {
        buildcurrent: function(player, controls, layers, media) {
            var t = this;
            
            $('<div class="mejs-time" role="timer" aria-live="off">' +
                    '<span class="mejs-currenttime">' + 
                        mejs.Utility.secondsToTimeCode(0, player.options) +
                    '</span>'+
                '</div>')
            .appendTo(controls);
            
            t.currenttime = t.controls.find('.mejs-currenttime');

            media.addEventListener('timeupdate',function() {
                player.updateCurrent();
            }, false);
        },


        buildduration: function(player, controls, layers, media) {
            var t = this;
            
            if (controls.children().last().find('.mejs-currenttime').length > 0) {
                $(t.options.timeAndDurationSeparator +
                    '<span class="mejs-duration">' + 
                        mejs.Utility.secondsToTimeCode(t.options.duration, t.options) +
                    '</span>')
                    .appendTo(controls.find('.mejs-time'));
            } else {

                // add class to current time
                controls.find('.mejs-currenttime').parent().addClass('mejs-currenttime-container');
                
                $('<div class="mejs-time mejs-duration-container">'+
                    '<span class="mejs-duration">' + 
                        mejs.Utility.secondsToTimeCode(t.options.duration, t.options) +
                    '</span>' +
                '</div>')
                .appendTo(controls);
            }
            
            t.durationD = t.controls.find('.mejs-duration');

            media.addEventListener('timeupdate',function() {
                player.updateDuration();
            }, false);
        },
        
        updateCurrent:  function() {
            var t = this;

            if (t.currenttime) {
                t.currenttime.html(mejs.Utility.secondsToTimeCode(t.media.currentTime, t.options));
            }
        },
        
        updateDuration: function() {
            var t = this;

            //Toggle the long video class if the video is longer than an hour.
            t.container.toggleClass("mejs-long-video", t.media.duration > 3600);
            
            if (t.durationD && (t.options.duration > 0 || t.media.duration)) {
                t.durationD.html(mejs.Utility.secondsToTimeCode(t.options.duration > 0 ? t.options.duration : t.media.duration, t.options));
            }       
        }
    });

})(mejs.$);

(function($) {

    $.extend(mejs.MepDefaults, {
        muteText: mejs.i18n.t('Mute Toggle'),
        allyVolumeControlText: mejs.i18n.t('Use Up/Down Arrow keys to increase or decrease volume.'),
        hideVolumeOnTouchDevices: true,
        
        audioVolume: 'horizontal',
        videoVolume: 'vertical'
    });

    $.extend(MediaElementPlayer.prototype, {
        buildvolume: function(player, controls, layers, media) {
                
            // Android and iOS don't support volume controls
            if ((mejs.MediaFeatures.isAndroid || mejs.MediaFeatures.isiOS) && this.options.hideVolumeOnTouchDevices)
                return;
            
            var t = this,
                mode = (t.isVideo) ? t.options.videoVolume : t.options.audioVolume,
                mute = (mode == 'horizontal') ?
                
                // horizontal version
                $('<div class="mejs-button mejs-volume-button mejs-mute">' +
                    '<button type="button" aria-controls="' + t.id + 
                        '" title="' + t.options.muteText + 
                        '" aria-label="' + t.options.muteText +
                    '"></button>'+
                '</div>' +
                  '<a href="javascript:void(0);" class="mejs-horizontal-volume-slider">' + // outer background
                    '<span class="mejs-offscreen">' + t.options.allyVolumeControlText + '</span>' +
                    '<div class="mejs-horizontal-volume-total"></div>'+ // line background
                    '<div class="mejs-horizontal-volume-current"></div>'+ // current volume
                    '<div class="mejs-horizontal-volume-handle"></div>'+ // handle
                '</a>'
                )
                    .appendTo(controls) :
                
                // vertical version
                $('<div class="mejs-button mejs-volume-button mejs-mute">'+
                    '<button type="button" aria-controls="' + t.id + 
                        '" title="' + t.options.muteText + 
                        '" aria-label="' + t.options.muteText + 
                    '"></button>'+
                    '<a href="javascript:void(0);" class="mejs-volume-slider">'+ // outer background
                        '<span class="mejs-offscreen">' + t.options.allyVolumeControlText + '</span>' +                  
                        '<div class="mejs-volume-total"></div>'+ // line background
                        '<div class="mejs-volume-current"></div>'+ // current volume
                        '<div class="mejs-volume-handle"></div>'+ // handle
                    '</a>'+
                '</div>')
                    .appendTo(controls),
            volumeSlider = t.container.find('.mejs-volume-slider, .mejs-horizontal-volume-slider'),
            volumeTotal = t.container.find('.mejs-volume-total, .mejs-horizontal-volume-total'),
            volumeCurrent = t.container.find('.mejs-volume-current, .mejs-horizontal-volume-current'),
            volumeHandle = t.container.find('.mejs-volume-handle, .mejs-horizontal-volume-handle'),

            positionVolumeHandle = function(volume, secondTry) {

                if (!volumeSlider.is(':visible') && typeof secondTry == 'undefined') {
                    volumeSlider.show();
                    positionVolumeHandle(volume, true);
                    volumeSlider.hide();
                    return;
                }

                // correct to 0-1
                volume = Math.max(0,volume);
                volume = Math.min(volume,1);

                // ajust mute button style
                if (volume === 0) {
                    mute.removeClass('mejs-mute').addClass('mejs-unmute');
                    mute.children('button').attr('title', mejs.i18n.t('Unmute')).attr('aria-label', mejs.i18n.t('Unmute'));
                } else {
                    mute.removeClass('mejs-unmute').addClass('mejs-mute');
                    mute.children('button').attr('title', mejs.i18n.t('Mute')).attr('aria-label', mejs.i18n.t('Mute'));
                }

                // top/left of full size volume slider background
                var totalPosition = volumeTotal.position();
                // position slider 
                if (mode == 'vertical') {
                    var
                    // height of the full size volume slider background
                        totalHeight = volumeTotal.height(),

                        // the new top position based on the current volume
                        // 70% volume on 100px height == top:30px
                        newTop = totalHeight - (totalHeight * volume);
    
                    // handle
                    volumeHandle.css('top', Math.round(totalPosition.top + newTop - (volumeHandle.height() / 2)));
    
                    // show the current visibility
                    volumeCurrent.height(totalHeight - newTop );
                    volumeCurrent.css('top', totalPosition.top + newTop);
                } else {
                    var
                        // height of the full size volume slider background
                        totalWidth = volumeTotal.width(),
                        
                        // the new left position based on the current volume
                        newLeft = totalWidth * volume;
    
                    // handle
                    volumeHandle.css('left', Math.round(totalPosition.left + newLeft - (volumeHandle.width() / 2)));
    
                    // rezize the current part of the volume bar
                    volumeCurrent.width( Math.round(newLeft) );
                }
            },
            handleVolumeMove = function(e) {
                
                var volume = null,
                    totalOffset = volumeTotal.offset();
                
                // calculate the new volume based on the moust position
                if (mode === 'vertical') {
                
                    var
                        railHeight = volumeTotal.height(),
                        newY = e.pageY - totalOffset.top;
                        
                    volume = (railHeight - newY) / railHeight;
                        
                    // the controls just hide themselves (usually when mouse moves too far up)
                    if (totalOffset.top === 0 || totalOffset.left === 0) {
                        return;
                    }
                    
                } else {
                    var
                        railWidth = volumeTotal.width(),
                        newX = e.pageX - totalOffset.left;
                        
                    volume = newX / railWidth;
                }
                
                // ensure the volume isn't outside 0-1
                volume = Math.max(0,volume);
                volume = Math.min(volume,1);
                
                // position the slider and handle
                positionVolumeHandle(volume);
                
                // set the media object (this will trigger the volumechanged event)
                if (volume === 0) {
                    media.setMuted(true);
                } else {
                    media.setMuted(false);
                }
                media.setVolume(volume);
            },
            mouseIsDown = false,
            mouseIsOver = false;

            // SLIDER
            
            mute
                .hover(function() {
                    volumeSlider.show();
                    mouseIsOver = true;
                }, function() {
                    mouseIsOver = false;
                        
                    if (!mouseIsDown && mode == 'vertical') {
                        volumeSlider.hide();
                    }
                });
            
            var updateVolumeSlider = function (e) {

                var volume = Math.floor(media.volume*100);

                volumeSlider.attr({
                    'aria-label': mejs.i18n.t('volumeSlider'),
                    'aria-valuemin': 0,
                    'aria-valuemax': 100,
                    'aria-valuenow': volume,
                    'aria-valuetext': volume+'%',
                    'role': 'slider',
                    'tabindex': 0
                });

            };
            
            volumeSlider
                .bind('mouseover', function() {
                    mouseIsOver = true; 
                })
                .bind('mousedown', function (e) {
                    handleVolumeMove(e);
                    t.globalBind('mousemove.vol', function(e) {
                        handleVolumeMove(e);
                    });
                    t.globalBind('mouseup.vol', function () {
                        mouseIsDown = false;
                        t.globalUnbind('.vol');

                        if (!mouseIsOver && mode == 'vertical') {
                            volumeSlider.hide();
                        }
                    });
                    mouseIsDown = true;
                        
                    return false;
                })
                .bind('keydown', function (e) {
                    var keyCode = e.keyCode;
                    var volume = media.volume;
                    switch (keyCode) {
                        case 38: // Up
                            volume += 0.1;
                            break;
                        case 40: // Down
                            volume = volume - 0.1;
                            break;
                        default:
                            return true;
                    }

                    mouseIsDown = false;
                    positionVolumeHandle(volume);
                    media.setVolume(volume);
                    return false;
                })
                .bind('blur', function () {
                    volumeSlider.hide();
                });

            // MUTE button
            mute.find('button').click(function() {
                media.setMuted( !media.muted );
            });
            
            //Keyboard input
            mute.find('button').bind('focus', function () {
                volumeSlider.show();
            });

            // listen for volume change events from other sources
            media.addEventListener('volumechange', function(e) {
                if (!mouseIsDown) {
                    if (media.muted) {
                        positionVolumeHandle(0);
                        mute.removeClass('mejs-mute').addClass('mejs-unmute');
                    } else {
                        positionVolumeHandle(media.volume);
                        mute.removeClass('mejs-unmute').addClass('mejs-mute');
                    }
                }
                updateVolumeSlider(e);
            }, false);
            
            // mutes the media and sets the volume icon muted if the initial volume is set to 0
            if (player.options.startVolume === 0) {
                media.setMuted(true);
            }
            
            // shim gets the startvolume as a parameter, but we have to set it on the native <video> and <audio> elements
            if (media.pluginType === 'native') {
                media.setVolume(player.options.startVolume);
            }
            
            t.container.on('controlsresize', function() {
                positionVolumeHandle(media.volume);
            });
        }
    });
    
})(mejs.$);

(function($) {

    $.extend(mejs.MepDefaults, {
        usePluginFullScreen: true,
        newWindowCallback: function() { return '';},
        fullscreenText: mejs.i18n.t('Fullscreen')
    });

    $.extend(MediaElementPlayer.prototype, {

        isFullScreen: false,

        isNativeFullScreen: false,

        isInIframe: false,

        buildfullscreen: function(player, controls, layers, media) {

            if (!player.isVideo)
                return;

            player.isInIframe = (window.location != window.parent.location);

            // native events
            if (mejs.MediaFeatures.hasTrueNativeFullScreen) {

                // chrome doesn't alays fire this in an iframe
                var func = function(e) {
                    if (player.isFullScreen) {
                        if (mejs.MediaFeatures.isFullScreen()) {
                            player.isNativeFullScreen = true;
                            // reset the controls once we are fully in full screen
                            player.setControlsSize();
                        } else {
                            player.isNativeFullScreen = false;
                            // when a user presses ESC
                            // make sure to put the player back into place
                            player.exitFullScreen();
                        }
                    }
                };

                player.globalBind(mejs.MediaFeatures.fullScreenEventName, func);
            }

            var t = this,
                fullscreenBtn =
                    $('<div class="mejs-button mejs-fullscreen-button">' +
                        '<button type="button" aria-controls="' + t.id + '" title="' + t.options.fullscreenText + '" aria-label="' + t.options.fullscreenText + '"></button>' +
                    '</div>')
                    .appendTo(controls);

                if (t.media.pluginType === 'native' || (!t.options.usePluginFullScreen && !mejs.MediaFeatures.isFirefox)) {

                    fullscreenBtn.click(function() {
                        var isFullScreen = (mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || player.isFullScreen;

                        if (isFullScreen) {
                            player.exitFullScreen();
                        } else {
                            player.enterFullScreen();
                        }
                    });

                } else {

                    var hideTimeout = null,
                        supportsPointerEvents = (function() {
                            // TAKEN FROM MODERNIZR
                            var element = document.createElement('x'),
                                documentElement = document.documentElement,
                                getComputedStyle = window.getComputedStyle,
                                supports;
                            if(!('pointerEvents' in element.style)){
                                return false;
                            }
                            element.style.pointerEvents = 'auto';
                            element.style.pointerEvents = 'x';
                            documentElement.appendChild(element);
                            supports = getComputedStyle &&
                                getComputedStyle(element, '').pointerEvents === 'auto';
                            documentElement.removeChild(element);
                            return !!supports;
                        })();

                    //

                    if (supportsPointerEvents && !mejs.MediaFeatures.isOpera) { // opera doesn't allow this :(

                        // allows clicking through the fullscreen button and controls down directly to Flash

                        /*
                         When a user puts his mouse over the fullscreen button, the controls are disabled
                         So we put a div over the video and another one on iether side of the fullscreen button
                         that caputre mouse movement
                         and restore the controls once the mouse moves outside of the fullscreen button
                        */

                        var fullscreenIsDisabled = false,
                            restoreControls = function() {
                                if (fullscreenIsDisabled) {
                                    // hide the hovers
                                    for (var i in hoverDivs) {
                                        hoverDivs[i].hide();
                                    }

                                    // restore the control bar
                                    fullscreenBtn.css('pointer-events', '');
                                    t.controls.css('pointer-events', '');

                                    // prevent clicks from pausing video
                                    t.media.removeEventListener('click', t.clickToPlayPauseCallback);

                                    // store for later
                                    fullscreenIsDisabled = false;
                                }
                            },
                            hoverDivs = {},
                            hoverDivNames = ['top', 'left', 'right', 'bottom'],
                            i, len,
                            positionHoverDivs = function() {
                                var fullScreenBtnOffsetLeft = fullscreenBtn.offset().left - t.container.offset().left,
                                    fullScreenBtnOffsetTop = fullscreenBtn.offset().top - t.container.offset().top,
                                    fullScreenBtnWidth = fullscreenBtn.outerWidth(true),
                                    fullScreenBtnHeight = fullscreenBtn.outerHeight(true),
                                    containerWidth = t.container.width(),
                                    containerHeight = t.container.height();

                                for (i in hoverDivs) {
                                    hoverDivs[i].css({position: 'absolute', top: 0, left: 0}); //, backgroundColor: '#f00'});
                                }

                                // over video, but not controls
                                hoverDivs['top']
                                    .width( containerWidth )
                                    .height( fullScreenBtnOffsetTop );

                                // over controls, but not the fullscreen button
                                hoverDivs['left']
                                    .width( fullScreenBtnOffsetLeft )
                                    .height( fullScreenBtnHeight )
                                    .css({top: fullScreenBtnOffsetTop});

                                // after the fullscreen button
                                hoverDivs['right']
                                    .width( containerWidth - fullScreenBtnOffsetLeft - fullScreenBtnWidth )
                                    .height( fullScreenBtnHeight )
                                    .css({top: fullScreenBtnOffsetTop,
                                         left: fullScreenBtnOffsetLeft + fullScreenBtnWidth});

                                // under the fullscreen button
                                hoverDivs['bottom']
                                    .width( containerWidth )
                                    .height( containerHeight - fullScreenBtnHeight - fullScreenBtnOffsetTop )
                                    .css({top: fullScreenBtnOffsetTop + fullScreenBtnHeight});
                            };

                        t.globalBind('resize', function() {
                            positionHoverDivs();
                        });

                        for (i = 0, len = hoverDivNames.length; i < len; i++) {
                            hoverDivs[hoverDivNames[i]] = $('<div class="mejs-fullscreen-hover" />').appendTo(t.container).mouseover(restoreControls).hide();
                        }

                        // on hover, kill the fullscreen button's HTML handling, allowing clicks down to Flash
                        fullscreenBtn.on('mouseover',function() {

                            if (!t.isFullScreen) {

                                var buttonPos = fullscreenBtn.offset(),
                                    containerPos = player.container.offset();

                                // move the button in Flash into place
                                media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, false);

                                // allows click through
                                fullscreenBtn.css('pointer-events', 'none');
                                t.controls.css('pointer-events', 'none');

                                // restore click-to-play
                                t.media.addEventListener('click', t.clickToPlayPauseCallback);

                                // show the divs that will restore things
                                for (i in hoverDivs) {
                                    hoverDivs[i].show();
                                }

                                positionHoverDivs();

                                fullscreenIsDisabled = true;
                            }

                        });

                        // restore controls anytime the user enters or leaves fullscreen
                        media.addEventListener('fullscreenchange', function(e) {
                            t.isFullScreen = !t.isFullScreen;
                            // don't allow plugin click to pause video - messes with
                            // plugin's controls
                            if (t.isFullScreen) {
                                t.media.removeEventListener('click', t.clickToPlayPauseCallback);
                            } else {
                                t.media.addEventListener('click', t.clickToPlayPauseCallback);
                            }
                            restoreControls();
                        });


                        // the mouseout event doesn't work on the fullscren button, because we already killed the pointer-events
                        // so we use the document.mousemove event to restore controls when the mouse moves outside the fullscreen button

                        t.globalBind('mousemove', function(e) {

                            // if the mouse is anywhere but the fullsceen button, then restore it all
                            if (fullscreenIsDisabled) {

                                var fullscreenBtnPos = fullscreenBtn.offset();


                                if (e.pageY < fullscreenBtnPos.top || e.pageY > fullscreenBtnPos.top + fullscreenBtn.outerHeight(true) ||
                                    e.pageX < fullscreenBtnPos.left || e.pageX > fullscreenBtnPos.left + fullscreenBtn.outerWidth(true)
                                    ) {

                                    fullscreenBtn.css('pointer-events', '');
                                    t.controls.css('pointer-events', '');

                                    fullscreenIsDisabled = false;
                                }
                            }
                        });



                    } else {

                        // the hover state will show the fullscreen button in Flash to hover up and click

                        fullscreenBtn
                            .on('mouseover', function() {

                                if (hideTimeout !== null) {
                                    clearTimeout(hideTimeout);
                                    delete hideTimeout;
                                }

                                var buttonPos = fullscreenBtn.offset(),
                                    containerPos = player.container.offset();

                                media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, true);

                            })
                            .on('mouseout', function() {

                                if (hideTimeout !== null) {
                                    clearTimeout(hideTimeout);
                                    delete hideTimeout;
                                }

                                hideTimeout = setTimeout(function() {
                                    media.hideFullscreenButton();
                                }, 1500);


                            });
                    }
                }

            player.fullscreenBtn = fullscreenBtn;

            t.globalBind('keydown',function (e) {
                if (((mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || t.isFullScreen) && e.keyCode == 27) {
                    player.exitFullScreen();
                }
            });
            
            t.normalHeight = 0;
            t.normalWidth = 0;

        },

        cleanfullscreen: function(player) {
            player.exitFullScreen();
        },

        containerSizeTimeout: null,

        enterFullScreen: function() {

            var t = this;

            // firefox+flash can't adjust plugin sizes without resetting :(
            if (t.media.pluginType !== 'native' && (mejs.MediaFeatures.isFirefox || t.options.usePluginFullScreen)) {
                //t.media.setFullscreen(true);
                //player.isFullScreen = true;
                return;
            }

            // set it to not show scroll bars so 100% will work
            $(document.documentElement).addClass('mejs-fullscreen');

            // store sizing
            t.normalHeight = t.container.height();
            t.normalWidth = t.container.width();

            // attempt to do true fullscreen (Safari 5.1 and Firefox Nightly only for now)
            if (t.media.pluginType === 'native') {
                if (mejs.MediaFeatures.hasTrueNativeFullScreen) {

                    mejs.MediaFeatures.requestFullScreen(t.container[0]);
                    //return;

                    if (t.isInIframe) {
                        // sometimes exiting from fullscreen doesn't work
                        // notably in Chrome <iframe>. Fixed in version 17
                        setTimeout(function checkFullscreen() {

                            if (t.isNativeFullScreen) {
                                var zoomMultiplier = window["devicePixelRatio"] || 1,
                                // Use a percent error margin since devicePixelRatio is a float and not exact.
                                    percentErrorMargin = 0.002, // 0.2%
                                    windowWidth = zoomMultiplier * $(window).width(),
                                    screenWidth = screen.width,
                                    // ** 13twelve
                                    // Screen width is sort of useless: http://www.quirksmode.org/blog/archives/2013/11/screenwidth_is.html
                                    // My rMBP ignores devicePixelRatio when returning the values, so fullscreen would always fail the "suddenly not fullscreen" test
                                    // Theory: the gap between reported values should give us an indication of browser behavior with screen.width and devicePixelRatio
                                    zoomedWindowWidth = zoomMultiplier * windowWidth;
                                    
                                if (Math.abs(screenWidth-windowWidth) > Math.abs(screenWidth-zoomedWindowWidth)) {
                                    // screen.width is likely true pixels, not CSS pixels, so we need to use the zoomed window width for comparison
                                    windowWidth = zoomedWindowWidth;
                                }
                                // ** / 13twelve

                                var absDiff = Math.abs(screenWidth - windowWidth),
                                    marginError = screenWidth * percentErrorMargin;

                                // check if the video is suddenly not really fullscreen
                                if (absDiff > marginError) {
                                    // manually exit
                                    t.exitFullScreen();
                                } else {
                                    // test again
                                    setTimeout(checkFullscreen, 500);
                                }
                            }
                            
                        }, 1000);
                    }

                } else if (mejs.MediaFeatures.hasSemiNativeFullScreen) {
                    t.media.webkitEnterFullscreen();
                    return;
                }
            }

            // check for iframe launch
            if (t.isInIframe) {
                var url = t.options.newWindowCallback(this);


                if (url !== '') {

                    // launch immediately
                    if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
                        t.pause();
                        window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
                        return;
                    } else {
                        setTimeout(function() {
                            if (!t.isNativeFullScreen) {
                                t.pause();
                                window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
                            }
                        }, 250);
                    }
                }

            }

            // full window code



            // make full size
            t.container
                .addClass('mejs-container-fullscreen')
                .width('100%')
                .height('100%');
                //.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000});

            // Only needed for safari 5.1 native full screen, can cause display issues elsewhere
            // Actually, it seems to be needed for IE8, too
            //if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
                t.containerSizeTimeout = setTimeout(function() {
                    t.container.css({width: '100%', height: '100%'});
                    t.setControlsSize();
                }, 500);
            //}

            if (t.media.pluginType === 'native') {
                t.$media
                    .width('100%')
                    .height('100%');
            } else {
                t.container.find('.mejs-shim')
                    .width('100%')
                    .height('100%');

                //if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
                    t.media.setVideoSize($(window).width(),$(window).height());
                //}
            }

            t.layers.children('div')
                .width('100%')
                .height('100%');

            if (t.fullscreenBtn) {
                t.fullscreenBtn
                    .removeClass('mejs-fullscreen')
                    .addClass('mejs-unfullscreen');
            }

            t.setControlsSize();
            t.isFullScreen = true;

            t.container.find('.mejs-captions-text').css('font-size', screen.width / t.width * 1.00 * 100 + '%');
            t.container.find('.mejs-captions-position').css('bottom', '45px');

            t.container.trigger('enteredfullscreen');
        },

        exitFullScreen: function() {

            var t = this;

            // Prevent container from attempting to stretch a second time
            clearTimeout(t.containerSizeTimeout);

            // firefox can't adjust plugins
            if (t.media.pluginType !== 'native' && mejs.MediaFeatures.isFirefox) {
                t.media.setFullscreen(false);
                //player.isFullScreen = false;
                return;
            }

            // come outo of native fullscreen
            if (mejs.MediaFeatures.hasTrueNativeFullScreen && (mejs.MediaFeatures.isFullScreen() || t.isFullScreen)) {
                mejs.MediaFeatures.cancelFullScreen();
            }

            // restore scroll bars to document
            $(document.documentElement).removeClass('mejs-fullscreen');

            t.container
                .removeClass('mejs-container-fullscreen')
                .width(t.normalWidth)
                .height(t.normalHeight);

            if (t.media.pluginType === 'native') {
                t.$media
                    .width(t.normalWidth)
                    .height(t.normalHeight);
            } else {
                t.container.find('.mejs-shim')
                    .width(t.normalWidth)
                    .height(t.normalHeight);

                t.media.setVideoSize(t.normalWidth, t.normalHeight);
            }

            t.layers.children('div')
                .width(t.normalWidth)
                .height(t.normalHeight);

            t.fullscreenBtn
                .removeClass('mejs-unfullscreen')
                .addClass('mejs-fullscreen');

            t.setControlsSize();
            t.isFullScreen = false;

            t.container.find('.mejs-captions-text').css('font-size','');
            t.container.find('.mejs-captions-position').css('bottom', '');

            t.container.trigger('exitedfullscreen');
        }
    });

})(mejs.$);

(function($) {

    // Speed
    $.extend(mejs.MepDefaults, {

        // We also support to pass object like this:
        // [{name: 'Slow', value: '0.75'}, {name: 'Normal', value: '1.00'}, ...]
        speeds: ['2.00', '1.50', '1.25', '1.00', '0.75'],

        defaultSpeed: '1.00',
        
        speedChar: 'x'

    });

    $.extend(MediaElementPlayer.prototype, {

        buildspeed: function(player, controls, layers, media) {
            var t = this;

            if (t.media.pluginType == 'native') {
                var 
                    speedButton = null,
                    speedSelector = null,
                    playbackSpeed = null,
                    inputId = null;

                var speeds = [];
                var defaultInArray = false;
                for (var i=0, len=t.options.speeds.length; i < len; i++) {
                    var s = t.options.speeds[i];
                    if (typeof(s) === 'string'){
                        speeds.push({
                            name: s + t.options.speedChar,
                            value: s
                        });
                        if(s === t.options.defaultSpeed) {
                            defaultInArray = true;
                        }
                    }
                    else {
                        speeds.push(s);
                        if(s.value === t.options.defaultSpeed) {
                            defaultInArray = true;
                        }
                    }
                }

                if (!defaultInArray) {
                    speeds.push({
                        name: t.options.defaultSpeed + t.options.speedChar,
                        value: t.options.defaultSpeed
                    });
                }

                speeds.sort(function(a, b) {
                    return parseFloat(b.value) - parseFloat(a.value);
                });

                var getSpeedNameFromValue = function(value) {
                    for(i=0,len=speeds.length; i <len; i++) {
                        if (speeds[i].value === value) {
                            return speeds[i].name;
                        }
                    }
                };

                var html = '<div class="mejs-button mejs-speed-button">' +
                            '<button type="button">' + getSpeedNameFromValue(t.options.defaultSpeed) + '</button>' +
                            '<div class="mejs-speed-selector">' +
                            '<ul>';

                for (i = 0, il = speeds.length; i<il; i++) {
                    inputId = t.id + '-speed-' + speeds[i].value;
                    html += '<li>' + 
                                '<input type="radio" name="speed" ' + 
                                            'value="' + speeds[i].value + '" ' +
                                            'id="' + inputId + '" ' +
                                            (speeds[i].value === t.options.defaultSpeed ? ' checked' : '') +
                                            ' />' +
                                '<label for="' + inputId + '" ' +
                                            (speeds[i].value === t.options.defaultSpeed ? ' class="mejs-speed-selected"' : '') +
                                            '>' + speeds[i].name + '</label>' +
                            '</li>';
                }
                html += '</ul></div></div>';

                speedButton = $(html).appendTo(controls);
                speedSelector = speedButton.find('.mejs-speed-selector');

                playbackSpeed = t.options.defaultSpeed;

                speedSelector
                    .on('click', 'input[type="radio"]', function() {
                        var newSpeed = $(this).attr('value');
                        playbackSpeed = newSpeed;
                        media.playbackRate = parseFloat(newSpeed);
                        speedButton.find('button').html(getSpeedNameFromValue(newSpeed));
                        speedButton.find('.mejs-speed-selected').removeClass('mejs-speed-selected');
                        speedButton.find('input[type="radio"]:checked').next().addClass('mejs-speed-selected');
                    });
                speedButton
                    .one( 'mouseenter focusin', function() {
                        speedSelector
                            .height(
                                speedButton.find('.mejs-speed-selector ul').outerHeight(true) +
                                speedButton.find('.mejs-speed-translations').outerHeight(true))
                            .css('top', (-1 * speedSelector.height()) + 'px');
                    });
            }
        }
    });

})(mejs.$);

(function($) {

    // add extra default options
    $.extend(mejs.MepDefaults, {
        // this will automatically turn on a <track>
        startLanguage: '',

        tracksText: mejs.i18n.t('Captions/Subtitles'),

        // By default, no WAI-ARIA live region - don't make a
        // screen reader speak captions over an audio track.
        tracksAriaLive: false,

        // option to remove the [cc] button when no <track kind="subtitles"> are present
        hideCaptionsButtonWhenEmpty: true,

        // If true and we only have one track, change captions to popup
        toggleCaptionsButtonWhenOnlyOne: false,

        // #id or .class
        slidesSelector: ''
    });

    $.extend(MediaElementPlayer.prototype, {

        hasChapters: false,

        cleartracks: function(player, controls, layers, media){
            if(player) {
                if(player.captions) player.captions.remove();
                if(player.chapters) player.chapters.remove();
                if(player.captionsText) player.captionsText.remove();
                if(player.captionsButton) player.captionsButton.remove();
            }
        },
        buildtracks: function(player, controls, layers, media) {
            if (player.tracks.length === 0)
                return;

            var t = this,
                attr = t.options.tracksAriaLive ?
                    'role="log" aria-live="assertive" aria-atomic="false"' : '',
                i;

            if (t.domNode.textTracks) { // if browser will do native captions, prefer mejs captions, loop through tracks and hide
                for (i = t.domNode.textTracks.length - 1; i >= 0; i--) {
                    t.domNode.textTracks[i].mode = "hidden";
                }
            }
            t.cleartracks(player, controls, layers, media);
            player.chapters =
                    $('<div class="mejs-chapters mejs-layer"></div>')
                        .prependTo(layers).hide();
            player.captions =
                    $('<div class="mejs-captions-layer mejs-layer"><div class="mejs-captions-position mejs-captions-position-hover" ' +
                    attr + '><span class="mejs-captions-text"></span></div></div>')
                        .prependTo(layers).hide();
            player.captionsText = player.captions.find('.mejs-captions-text');
            player.captionsButton =
                    $('<div class="mejs-button mejs-captions-button">'+
                        '<button type="button" aria-controls="' + t.id + '" title="' + t.options.tracksText + '" aria-label="' + t.options.tracksText + '"></button>'+
                        '<div class="mejs-captions-selector">'+
                            '<ul>'+
                                '<li>'+
                                    '<input type="radio" name="' + player.id + '_captions" id="' + player.id + '_captions_none" value="none" checked="checked" />' +
                                    '<label for="' + player.id + '_captions_none">' + mejs.i18n.t('None') +'</label>'+
                                '</li>' +
                            '</ul>'+
                        '</div>'+
                    '</div>')
                        .appendTo(controls);


            var subtitleCount = 0;
            for (i=0; i<player.tracks.length; i++) {
                if (player.tracks[i].kind == 'subtitles') {
                    subtitleCount++;
                }
            }

            // if only one language then just make the button a toggle
            if (t.options.toggleCaptionsButtonWhenOnlyOne && subtitleCount == 1){
                // click
                player.captionsButton.on('click',function() {
                    if (player.selectedTrack === null) {
                        lang = player.tracks[0].srclang;
                    } else {
                        lang = 'none';
                    }
                    player.setTrack(lang);
                });
            } else {
                // hover or keyboard focus
                player.captionsButton.on( 'mouseenter focusin', function() {
                    $(this).find('.mejs-captions-selector').css('visibility','visible');
                })

                // handle clicks to the language radio buttons
                .on('click','input[type=radio]',function() {
                    lang = this.value;
                    player.setTrack(lang);
                });

                player.captionsButton.on( 'mouseleave focusout', function() {
                    $(this).find(".mejs-captions-selector").css("visibility","hidden");
                });

            }

            if (!player.options.alwaysShowControls) {
                // move with controls
                player.container
                    .bind('controlsshown', function () {
                        // push captions above controls
                        player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');

                    })
                    .bind('controlshidden', function () {
                        if (!media.paused) {
                            // move back to normal place
                            player.container.find('.mejs-captions-position').removeClass('mejs-captions-position-hover');
                        }
                    });
            } else {
                player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
            }

            player.trackToLoad = -1;
            player.selectedTrack = null;
            player.isLoadingTrack = false;

            // add to list
            for (i=0; i<player.tracks.length; i++) {
                if (player.tracks[i].kind == 'subtitles') {
                    player.addTrackButton(player.tracks[i].srclang, player.tracks[i].label);
                }
            }

            // start loading tracks
            player.loadNextTrack();

            media.addEventListener('timeupdate',function(e) {
                player.displayCaptions();
            }, false);

            if (player.options.slidesSelector !== '') {
                player.slidesContainer = $(player.options.slidesSelector);

                media.addEventListener('timeupdate',function(e) {
                    player.displaySlides();
                }, false);

            }

            media.addEventListener('loadedmetadata', function(e) {
                player.displayChapters();
            }, false);

            player.container.hover(
                function () {
                    // chapters
                    if (player.hasChapters) {
                        player.chapters.css('visibility','visible');
                        player.chapters.fadeIn(200).height(player.chapters.find('.mejs-chapter').outerHeight());
                    }
                },
                function () {
                    if (player.hasChapters && !media.paused) {
                        player.chapters.fadeOut(200, function() {
                            $(this).css('visibility','hidden');
                            $(this).css('display','block');
                        });
                    }
                });

            t.container.on('controlsresize', function() {
                t.adjustLanguageBox();
            });
            
            // check for autoplay
            if (player.node.getAttribute('autoplay') !== null) {
                player.chapters.css('visibility','hidden');
            }
        },

        setTrack: function(lang){

            var t = this,
                i;

            if (lang == 'none') {
                t.selectedTrack = null;
                t.captionsButton.removeClass('mejs-captions-enabled');
            } else {
                for (i=0; i<t.tracks.length; i++) {
                    if (t.tracks[i].srclang == lang) {
                        if (t.selectedTrack === null)
                            t.captionsButton.addClass('mejs-captions-enabled');
                        t.selectedTrack = t.tracks[i];
                        t.captions.attr('lang', t.selectedTrack.srclang);
                        t.displayCaptions();
                        break;
                    }
                }
            }
        },

        loadNextTrack: function() {
            var t = this;

            t.trackToLoad++;
            if (t.trackToLoad < t.tracks.length) {
                t.isLoadingTrack = true;
                t.loadTrack(t.trackToLoad);
            } else {
                // add done?
                t.isLoadingTrack = false;

                t.checkForTracks();
            }
        },

        loadTrack: function(index){
            var
                t = this,
                track = t.tracks[index],
                after = function() {

                    track.isLoaded = true;

                    t.enableTrackButton(track.srclang, track.label);

                    t.loadNextTrack();

                };


            $.ajax({
                url: track.src,
                dataType: "text",
                success: function(d) {

                    // parse the loaded file
                    if (typeof d == "string" && (/<tt\s+xml/ig).exec(d)) {
                        track.entries = mejs.TrackFormatParser.dfxp.parse(d);
                    } else {
                        track.entries = mejs.TrackFormatParser.webvtt.parse(d);
                    }

                    after();

                    if (track.kind == 'chapters') {
                        t.media.addEventListener('play', function(e) {
                            if (t.media.duration > 0) {
                                t.displayChapters(track);
                            }
                        }, false);
                    }

                    if (track.kind == 'slides') {
                        t.setupSlides(track);
                    }
                },
                error: function() {
                    t.removeTrackButton(track.srclang);
                    t.loadNextTrack();
                }
            });
        },

        enableTrackButton: function(lang, label) {
            var t = this;

            if (label === '') {
                label = mejs.language.codes[lang] || lang;
            }

            t.captionsButton
                .find('input[value=' + lang + ']')
                    .prop('disabled',false)
                .siblings('label')
                    .html( label );

            // auto select
            if (t.options.startLanguage == lang) {
                $('#' + t.id + '_captions_' + lang).prop('checked', true).trigger('click');
            }

            t.adjustLanguageBox();
        },
        
        removeTrackButton: function(lang) {
            var t = this;
            
            t.captionsButton.find('input[value=' + lang + ']').closest('li').remove();
            
            t.adjustLanguageBox();
        },

        addTrackButton: function(lang, label) {
            var t = this;
            if (label === '') {
                label = mejs.language.codes[lang] || lang;
            }

            t.captionsButton.find('ul').append(
                $('<li>'+
                    '<input type="radio" name="' + t.id + '_captions" id="' + t.id + '_captions_' + lang + '" value="' + lang + '" disabled="disabled" />' +
                    '<label for="' + t.id + '_captions_' + lang + '">' + label + ' (loading)' + '</label>'+
                '</li>')
            );

            t.adjustLanguageBox();

            // remove this from the dropdownlist (if it exists)
            t.container.find('.mejs-captions-translations option[value=' + lang + ']').remove();
        },

        adjustLanguageBox:function() {
            var t = this;
            // adjust the size of the outer box
            t.captionsButton.find('.mejs-captions-selector').height(
                t.captionsButton.find('.mejs-captions-selector ul').outerHeight(true) +
                t.captionsButton.find('.mejs-captions-translations').outerHeight(true)
            );
        },

        checkForTracks: function() {
            var
                t = this,
                hasSubtitles = false;

            // check if any subtitles
            if (t.options.hideCaptionsButtonWhenEmpty) {
                for (i=0; i<t.tracks.length; i++) {
                    if (t.tracks[i].kind == 'subtitles' && t.tracks[i].isLoaded) {
                        hasSubtitles = true;
                        break;
                    }
                }

                if (!hasSubtitles) {
                    t.captionsButton.hide();
                    t.setControlsSize();
                }
            }
        },

        displayCaptions: function() {

            if (typeof this.tracks == 'undefined')
                return;

            var
                t = this,
                i,
                track = t.selectedTrack;

            if (track !== null && track.isLoaded) {
                for (i=0; i<track.entries.times.length; i++) {
                    if (t.media.currentTime >= track.entries.times[i].start && t.media.currentTime <= track.entries.times[i].stop) {
                        // Set the line before the timecode as a class so the cue can be targeted if needed
                        t.captionsText.html(track.entries.text[i]).attr('class', 'mejs-captions-text ' + (track.entries.times[i].identifier || ''));
                        t.captions.show().height(0);
                        return; // exit out if one is visible;
                    }
                }
                t.captions.hide();
            } else {
                t.captions.hide();
            }
        },

        setupSlides: function(track) {
            var t = this;

            t.slides = track;
            t.slides.entries.imgs = [t.slides.entries.text.length];
            t.showSlide(0);

        },

        showSlide: function(index) {
            if (typeof this.tracks == 'undefined' || typeof this.slidesContainer == 'undefined') {
                return;
            }

            var t = this,
                url = t.slides.entries.text[index],
                img = t.slides.entries.imgs[index];

            if (typeof img == 'undefined' || typeof img.fadeIn == 'undefined') {

                t.slides.entries.imgs[index] = img = $('<img src="' + url + '">')
                        .on('load', function() {
                            img.appendTo(t.slidesContainer)
                                .hide()
                                .fadeIn()
                                .siblings(':visible')
                                    .fadeOut();

                        });

            } else {

                if (!img.is(':visible') && !img.is(':animated')) {

                    //

                    img.fadeIn()
                        .siblings(':visible')
                            .fadeOut();
                }
            }

        },

        displaySlides: function() {

            if (typeof this.slides == 'undefined')
                return;

            var
                t = this,
                slides = t.slides,
                i;

            for (i=0; i<slides.entries.times.length; i++) {
                if (t.media.currentTime >= slides.entries.times[i].start && t.media.currentTime <= slides.entries.times[i].stop){

                    t.showSlide(i);

                    return; // exit out if one is visible;
                }
            }
        },

        displayChapters: function() {
            var
                t = this,
                i;

            for (i=0; i<t.tracks.length; i++) {
                if (t.tracks[i].kind == 'chapters' && t.tracks[i].isLoaded) {
                    t.drawChapters(t.tracks[i]);
                    t.hasChapters = true;
                    break;
                }
            }
        },

        drawChapters: function(chapters) {
            var
                t = this,
                i,
                dur,
                //width,
                //left,
                percent = 0,
                usedPercent = 0;

            t.chapters.empty();

            for (i=0; i<chapters.entries.times.length; i++) {
                dur = chapters.entries.times[i].stop - chapters.entries.times[i].start;
                percent = Math.floor(dur / t.media.duration * 100);
                if (percent + usedPercent > 100 || // too large
                    i == chapters.entries.times.length-1 && percent + usedPercent < 100) // not going to fill it in
                    {
                    percent = 100 - usedPercent;
                }
                //width = Math.floor(t.width * dur / t.media.duration);
                //left = Math.floor(t.width * chapters.entries.times[i].start / t.media.duration);
                //if (left + width > t.width) {
                //  width = t.width - left;
                //}

                t.chapters.append( $(
                    '<div class="mejs-chapter" rel="' + chapters.entries.times[i].start + '" style="left: ' + usedPercent.toString() + '%;width: ' + percent.toString() + '%;">' +
                        '<div class="mejs-chapter-block' + ((i==chapters.entries.times.length-1) ? ' mejs-chapter-block-last' : '') + '">' +
                            '<span class="ch-title">' + chapters.entries.text[i] + '</span>' +
                            '<span class="ch-time">' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].start, t.options) + '&ndash;' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].stop, t.options) + '</span>' +
                        '</div>' +
                    '</div>'));
                usedPercent += percent;
            }

            t.chapters.find('div.mejs-chapter').click(function() {
                t.media.setCurrentTime( parseFloat( $(this).attr('rel') ) );
                if (t.media.paused) {
                    t.media.play();
                }
            });

            t.chapters.show();
        }
    });



    mejs.language = {
        codes:  {
            af:'Afrikaans',
            sq:'Albanian',
            ar:'Arabic',
            be:'Belarusian',
            bg:'Bulgarian',
            ca:'Catalan',
            zh:'Chinese',
            'zh-cn':'Chinese Simplified',
            'zh-tw':'Chinese Traditional',
            hr:'Croatian',
            cs:'Czech',
            da:'Danish',
            nl:'Dutch',
            en:'English',
            et:'Estonian',
            fl:'Filipino',
            fi:'Finnish',
            fr:'French',
            gl:'Galician',
            de:'German',
            el:'Greek',
            ht:'Haitian Creole',
            iw:'Hebrew',
            hi:'Hindi',
            hu:'Hungarian',
            is:'Icelandic',
            id:'Indonesian',
            ga:'Irish',
            it:'Italian',
            ja:'Japanese',
            ko:'Korean',
            lv:'Latvian',
            lt:'Lithuanian',
            mk:'Macedonian',
            ms:'Malay',
            mt:'Maltese',
            no:'Norwegian',
            fa:'Persian',
            pl:'Polish',
            pt:'Portuguese',
            // 'pt-pt':'Portuguese (Portugal)',
            ro:'Romanian',
            ru:'Russian',
            sr:'Serbian',
            sk:'Slovak',
            sl:'Slovenian',
            es:'Spanish',
            sw:'Swahili',
            sv:'Swedish',
            tl:'Tagalog',
            th:'Thai',
            tr:'Turkish',
            uk:'Ukrainian',
            vi:'Vietnamese',
            cy:'Welsh',
            yi:'Yiddish'
        }
    };

    /*
    Parses WebVTT format which should be formatted as
    ================================
    WEBVTT

    1
    00:00:01,1 --> 00:00:05,000
    A line of text

    2
    00:01:15,1 --> 00:02:05,000
    A second line of text

    ===============================

    Adapted from: http://www.delphiki.com/html5/playr
    */
    mejs.TrackFormatParser = {
        webvtt: {
            pattern_timecode: /^((?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}([,.][0-9]{1,3})?) --\> ((?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}([,.][0-9]{3})?)(.*)$/,

            parse: function(trackText) {
                var
                    i = 0,
                    lines = mejs.TrackFormatParser.split2(trackText, /\r?\n/),
                    entries = {text:[], times:[]},
                    timecode,
                    text,
                    identifier;
                for(; i<lines.length; i++) {
                    timecode = this.pattern_timecode.exec(lines[i]);

                    if (timecode && i<lines.length) {
                        if ((i - 1) >= 0 && lines[i - 1] !== '') {
                            identifier = lines[i - 1];
                        }
                        i++;
                        // grab all the (possibly multi-line) text that follows
                        text = lines[i];
                        i++;
                        while(lines[i] !== '' && i<lines.length){
                            text = text + '\n' + lines[i];
                            i++;
                        }
                        text = $.trim(text).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>");
                        // Text is in a different array so I can use .join
                        entries.text.push(text);
                        entries.times.push(
                        {
                            identifier: identifier,
                            start: (mejs.Utility.convertSMPTEtoSeconds(timecode[1]) === 0) ? 0.200 : mejs.Utility.convertSMPTEtoSeconds(timecode[1]),
                            stop: mejs.Utility.convertSMPTEtoSeconds(timecode[3]),
                            settings: timecode[5]
                        });
                    }
                    identifier = '';
                }
                return entries;
            }
        },
        // Thanks to Justin Capella: https://github.com/johndyer/mediaelement/pull/420
        dfxp: {
            parse: function(trackText) {
                trackText = $(trackText).filter("tt");
                var
                    i = 0,
                    container = trackText.children("div").eq(0),
                    lines = container.find("p"),
                    styleNode = trackText.find("#" + container.attr("style")),
                    styles,
                    text,
                    entries = {text:[], times:[]};


                if (styleNode.length) {
                    var attributes = styleNode.removeAttr("id").get(0).attributes;
                    if (attributes.length) {
                        styles = {};
                        for (i = 0; i < attributes.length; i++) {
                            styles[attributes[i].name.split(":")[1]] = attributes[i].value;
                        }
                    }
                }

                for(i = 0; i<lines.length; i++) {
                    var style;
                    var _temp_times = {
                        start: null,
                        stop: null,
                        style: null
                    };
                    if (lines.eq(i).attr("begin")) _temp_times.start = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i).attr("begin"));
                    if (!_temp_times.start && lines.eq(i-1).attr("end")) _temp_times.start = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i-1).attr("end"));
                    if (lines.eq(i).attr("end")) _temp_times.stop = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i).attr("end"));
                    if (!_temp_times.stop && lines.eq(i+1).attr("begin")) _temp_times.stop = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i+1).attr("begin"));
                    if (styles) {
                        style = "";
                        for (var _style in styles) {
                            style += _style + ":" + styles[_style] + ";";
                        }
                    }
                    if (style) _temp_times.style = style;
                    if (_temp_times.start === 0) _temp_times.start = 0.200;
                    entries.times.push(_temp_times);
                    text = $.trim(lines.eq(i).html()).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>");
                    entries.text.push(text);
                    if (entries.times.start === 0) entries.times.start = 2;
                }
                return entries;
            }
        },
        split2: function (text, regex) {
            // normal version for compliant browsers
            // see below for IE fix
            return text.split(regex);
        }
    };

    // test for browsers with bad String.split method.
    if ('x\n\ny'.split(/\n/gi).length != 3) {
        // add super slow IE8 and below version
        mejs.TrackFormatParser.split2 = function(text, regex) {
            var
                parts = [],
                chunk = '',
                i;

            for (i=0; i<text.length; i++) {
                chunk += text.substring(i,i+1);
                if (regex.test(chunk)) {
                    parts.push(chunk.replace(regex, ''));
                    chunk = '';
                }
            }
            parts.push(chunk);
            return parts;
        };
    }

})(mejs.$);

/*
* ContextMenu Plugin
* 
*
*/

(function($) {

$.extend(mejs.MepDefaults,
    { 'contextMenuItems': [
        // demo of a fullscreen option
        { 
            render: function(player) {
                
                // check for fullscreen plugin
                if (typeof player.enterFullScreen == 'undefined')
                    return null;
            
                if (player.isFullScreen) {
                    return mejs.i18n.t('Turn off Fullscreen');
                } else {
                    return mejs.i18n.t('Go Fullscreen');
                }
            },
            click: function(player) {
                if (player.isFullScreen) {
                    player.exitFullScreen();
                } else {
                    player.enterFullScreen();
                }
            }
        }
        ,
        // demo of a mute/unmute button
        { 
            render: function(player) {
                if (player.media.muted) {
                    return mejs.i18n.t('Unmute');
                } else {
                    return mejs.i18n.t('Mute');
                }
            },
            click: function(player) {
                if (player.media.muted) {
                    player.setMuted(false);
                } else {
                    player.setMuted(true);
                }
            }
        },
        // separator
        {
            isSeparator: true
        }
        ,
        // demo of simple download video
        { 
            render: function(player) {
                return mejs.i18n.t('Download Video');
            },
            click: function(player) {
                window.location.href = player.media.currentSrc;
            }
        }   
    ]}
);


    $.extend(MediaElementPlayer.prototype, {
        buildcontextmenu: function(player, controls, layers, media) {
            
            // create context menu
            player.contextMenu = $('<div class="mejs-contextmenu"></div>')
                                .appendTo($('body'))
                                .hide();
            
            // create events for showing context menu
            player.container.bind('contextmenu', function(e) {
                if (player.isContextMenuEnabled) {
                    e.preventDefault();
                    player.renderContextMenu(e.clientX-1, e.clientY-1);
                    return false;
                }
            });
            player.container.bind('click', function() {
                player.contextMenu.hide();
            }); 
            player.contextMenu.bind('mouseleave', function() {

                //
                player.startContextMenuTimer();
                
            });     
        },

        cleancontextmenu: function(player) {
            player.contextMenu.remove();
        },
        
        isContextMenuEnabled: true,
        enableContextMenu: function() {
            this.isContextMenuEnabled = true;
        },
        disableContextMenu: function() {
            this.isContextMenuEnabled = false;
        },
        
        contextMenuTimeout: null,
        startContextMenuTimer: function() {
            //
            
            var t = this;
            
            t.killContextMenuTimer();
            
            t.contextMenuTimer = setTimeout(function() {
                t.hideContextMenu();
                t.killContextMenuTimer();
            }, 750);
        },
        killContextMenuTimer: function() {
            var timer = this.contextMenuTimer;
            
            //
            
            if (timer != null) {                
                clearTimeout(timer);
                delete timer;
                timer = null;
            }
        },      
        
        hideContextMenu: function() {
            this.contextMenu.hide();
        },
        
        renderContextMenu: function(x,y) {
            
            // alway re-render the items so that things like "turn fullscreen on" and "turn fullscreen off" are always written correctly
            var t = this,
                html = '',
                items = t.options.contextMenuItems;
            
            for (var i=0, il=items.length; i<il; i++) {
                
                if (items[i].isSeparator) {
                    html += '<div class="mejs-contextmenu-separator"></div>';
                } else {
                
                    var rendered = items[i].render(t);
                
                    // render can return null if the item doesn't need to be used at the moment
                    if (rendered != null) {
                        html += '<div class="mejs-contextmenu-item" data-itemindex="' + i + '" id="element-' + (Math.random()*1000000) + '">' + rendered + '</div>';
                    }
                }
            }
            
            // position and show the context menu
            t.contextMenu
                .empty()
                .append($(html))
                .css({top:y, left:x})
                .show();
                
            // bind events
            t.contextMenu.find('.mejs-contextmenu-item').each(function() {
                            
                // which one is this?
                var $dom = $(this),
                    itemIndex = parseInt( $dom.data('itemindex'), 10 ),
                    item = t.options.contextMenuItems[itemIndex];
                
                // bind extra functionality?
                if (typeof item.show != 'undefined')
                    item.show( $dom , t);
                
                // bind click action
                $dom.click(function() {         
                    // perform click action
                    if (typeof item.click != 'undefined')
                        item.click(t);
                    
                    // close
                    t.contextMenu.hide();               
                });             
            }); 
            
            // stop the controls from hiding
            setTimeout(function() {
                t.killControlsTimer('rev3');    
            }, 100);
                        
        }
    });
    
})(mejs.$);
(function($) {
    // skip back button

    $.extend(mejs.MepDefaults, {
        skipBackInterval: 30,
        // %1 will be replaced with skipBackInterval in this string
        skipBackText: mejs.i18n.t('Skip back %1 seconds')
    });

    $.extend(MediaElementPlayer.prototype, {
        buildskipback: function(player, controls, layers, media) {
            var
                t = this,
                // Replace %1 with skip back interval
                backText = t.options.skipBackText.replace('%1', t.options.skipBackInterval),
                // create the loop button
                loop =
                $('<div class="mejs-button mejs-skip-back-button">' +
                    '<button type="button" aria-controls="' + t.id + '" title="' + backText + '" aria-label="' + backText + '">' + t.options.skipBackInterval + '</button>' +
                '</div>')
                // append it to the toolbar
                .appendTo(controls)
                // add a click toggle event
                .click(function() {
                    media.setCurrentTime(Math.max(media.currentTime - t.options.skipBackInterval, 0));
                    $(this).find('button').blur();
                });
        }
    });

})(mejs.$);

/**
 * Postroll plugin
 */
(function($) {

    $.extend(mejs.MepDefaults, {
        postrollCloseText: mejs.i18n.t('Close')
    });

    // Postroll
    $.extend(MediaElementPlayer.prototype, {
        buildpostroll: function(player, controls, layers, media) {
            var
                t = this,
                postrollLink = t.container.find('link[rel="postroll"]').attr('href');

            if (typeof postrollLink !== 'undefined') {
                player.postroll =
                    $('<div class="mejs-postroll-layer mejs-layer"><a class="mejs-postroll-close" onclick="$(this).parent().hide();return false;">' + t.options.postrollCloseText + '</a><div class="mejs-postroll-layer-content"></div></div>').prependTo(layers).hide();

                t.media.addEventListener('ended', function (e) {
                    $.ajax({
                        dataType: 'html',
                        url: postrollLink,
                        success: function (data, textStatus) {
                            layers.find('.mejs-postroll-layer-content').html(data);
                        }
                    });
                    player.postroll.show();
                }, false);
            }
        }
    });

})(mejs.$);